2012-09-24 63 views
4

背景:我在iOS 5中開始了我的項目,並構建了一個帶有圖層的美麗按鈕。我添加了一個textLayer到按鈕並且使用以下代碼居中:來自iOS 6上CATextLayer上的不需要的垂直填充

float textLayerVerticlePadding = ((self.bounds.size.height - fontSize) /2); 
    textLayer = [[CATextLayer alloc]init]; 
    [textLayer setFrame:CGRectOffset(self.bounds, 0, textLayerVerticlePadding)]; 

它的工作原理極大,看起來死點直到的iOS 6.

問題: iOS 6的加入之間的空間(填充)最上面的邊界和textLayer中的文本。這擾亂了上面的計算。有沒有辦法確保iOS 6不?因爲我想支持iOS 5和6(對於喜歡Google Map的用戶)。

圖片:
這一個是iOS 5及紅色是textLayer的背景(使它更明顯) enter image description here

而這一次是iOS 6的
enter image description here


更新:雖然以下所有答案都是以他們自己的方式正確的,但我fou通過最簡單的方式來發布帖子來執行此操作。 HelveticaNeue爲iOS5和iOS6留下了一點空間,與Helvetica不同,它在iOS5的頂部留下空間並且在iOS6中留下一點空間。

更新2:玩了一會兒,發現了一點空間的大小。沒有深入細節,空間是您的字體大小的1/6。所以爲了彌補它,我寫了

float textLayerVerticlePadding = ((self.bounds.size.height - fontSize) /2) - (fontSize/6); 
[textLayer setFrame:CGRectOffset(self.bounds, 0, textLayerVerticlePadding)]; 

有了這段代碼,我每次都得到一個死亡中心。請注意,這僅在iOS5和iOS6上使用HelveticaNeue-Bold進行測試。我不能說別的。

+1

快速回答:使用HelveticaNeue家庭安裝的系統字體(這是有字體名稱「.HelveticaNeueUI」)。長答案:見下文。 – t0rst

+0

你好,如果我想在頂部添加更多填充以使HELLO位於紅色框的中心,即vertical align = center? –

回答

8

在iOS 5及前,在CATextLayer第一基線總是從邊界的由上升得到的頂部向下定位從CTLineGetTypographicBounds時通過一個CTLine用字符串作爲第一行。

在iOS 6中,這不適用於所有字體。因此,當您定位CATextLayer時,您無法再可靠地決定將其放置在何處以獲得正確的視覺對齊。或者你能嗎? ...

首先,旁白:在前段時間在iOS 5中試圖解決CATextLayer的定位行爲時,我嘗試使用帽高度,UIFont上升的所有組合等,最終發現從CTLineGetTypographicBounds是我需要的。在這個過程中,我發現a)從UIFont ascender,CTFontGetAscentCTLineGetTypographicBounds的上升對於某些字體是不一致的,以及b)上升經常是奇怪的 - 無論是裁剪口音還是留在上面的空間。 a)的解決方案是知道使用哪個值。除了通過抵消CATextLayer範圍來留出足夠的空間,如果它可能具有被修剪的口音,那麼對b)沒有真正的解決方案。

返回iOS 6.如果您避免了最糟糕的字體(從6.0開始,並且可能會發生變化),仍然可以使用CATextLayer與其他字體進行編程式定位。罪犯是:AcademyEngravedLetPlain快遞HoeflerText帕拉蒂諾 - 視覺,這些家庭位置正確(不含即裁剪)在CATextLayer,但沒有三個上升來源爲您提供了基線其中的一個可用的指示被放置。 Helvetica.HelveticaNeueUI(aka系統字體)系列在由UIFont ascender給出的上升沿正確定位,但其他上升源不被使用。

一些來自我做過測試的例子。示例文本以不同顏色繪製三次。座標原點是灰色框的左上角。黑色文字由CTLineDraw繪製,向下偏移CTLineGetTypographicBounds;透明紅色由CATextLayer繪製,邊界等於灰色框;透明藍色是用UIKitNSString加上drawAtPoint:withFont:定位在灰色框的原點和UIFont

1)表現良好的字體Copperplate-Light。這三個樣本是重合的,給了栗色,並且意味着所有來源的攀登距離都相近。同爲iOS 5和6

copperplate-light iOS 6

2)快遞的iOS 5 CATextLayer位置文本太高(紅色),但CTLineDrawCTLineGetTypographicBounds(黑色上升)下匹配CATextLayer定位 - 所以我們可以放置並從那裏改正。NSString drawAtPoint:withFont:(藍色)放置文本而不削減。 (黑體.HelveticaNeueUI這樣的表現在iOS 6中)

courier iOS 5

3)快遞的iOS 6 CATextLayer(紅色),現在下放置文本,以便它不會被截斷,但該定位不再匹配CTLineGetTypographicBounds(黑色)的上升或NSString drawAtPoint:withFont:(藍色)中使用的UIFont上升的匹配。這對於編程定位是不可用的。 (AcademyEngravedLetPlainHoeflerText帕拉蒂諾也是這樣的表現在iOS 6中)

courier iOS 6

希望這有助於避免一些浪費時間,我所經歷的時間,如果你想浸在更深的一點,有這個發揮:

- (NSString*)reportInconsistentFontAscents 
{ 
    NSMutableString*   results; 
    NSMutableArray*    fontNameArray; 
    CGFloat      fontSize = 28; 
    NSString*     fn; 
    NSString*     sample = @"Éa3Çy"; 
    CFRange      range; 
    NSMutableAttributedString* mas; 
    UIFont*      uifont; 
    CTFontRef     ctfont; 
    CTLineRef     ctline; 
    CGFloat      uif_ascent; 
    CGFloat      ctfont_ascent; 
    CGFloat      ctline_ascent; 

    results = [NSMutableString stringWithCapacity: 10000]; 
    mas = [[NSMutableAttributedString alloc] initWithString: sample]; 
    range.location = 0, range.length = [sample length]; 

    fontNameArray = [NSMutableArray arrayWithCapacity: 250]; 
    for (fn in [UIFont familyNames]) 
     [fontNameArray addObjectsFromArray: [UIFont fontNamesForFamilyName: fn]]; 
    [fontNameArray sortUsingSelector: @selector(localizedCaseInsensitiveCompare:)]; 
    [fontNameArray addObject: [UIFont systemFontOfSize: fontSize].fontName]; 
    [fontNameArray addObject: [UIFont italicSystemFontOfSize: fontSize].fontName]; 
    [fontNameArray addObject: [UIFont boldSystemFontOfSize: fontSize].fontName]; 

    [results appendString: @"Font name\tUIFA\tCTFA\tCTLA"]; 

    for (fn in fontNameArray) 
    { 
     uifont = [UIFont fontWithName: fn size: fontSize]; 
     uif_ascent = uifont.ascender; 

     ctfont = CTFontCreateWithName((CFStringRef)fn, fontSize, NULL); 
     ctfont_ascent = CTFontGetAscent(ctfont); 

     CFAttributedStringSetAttribute((CFMutableAttributedStringRef)mas, range, kCTFontAttributeName, ctfont); 
     ctline = CTLineCreateWithAttributedString((CFAttributedStringRef)mas); 
     ctline_ascent = 0; 
     CTLineGetTypographicBounds(ctline, &ctline_ascent, 0, 0); 

     [results appendFormat: @"\n%@\t%.3f\t%.3f\t%.3f", fn, uif_ascent, ctfont_ascent, ctline_ascent]; 

     if (fabsf(uif_ascent - ctfont_ascent) >= .5f // >.5 can round to pixel diffs in display 
     || fabsf(uif_ascent - ctline_ascent) >= .5f) 
      [results appendString: @"\t*****"]; 

     CFRelease(ctline); 
     CFRelease(ctfont); 
    } 

    [mas release]; 

    return results; 
} 
+0

你好,是否可以垂直對齊中心你好在紅色框? –

+0

@VanDuTran上面的答案都是關於在您的CATextLayer.bounds.origin爲0,0時預測(儘可能可靠地)文本基線的垂直位置。一旦你知道了這一點,你就可以計算出你想要的基線位置(使用字體的頂點高度,上升,可用面積等),然後調整CATextLayer.bounds。 origin.y將基線偏移到正確的位置。 – t0rst

+0

我會嘗試,因爲在我的情況下,我希望紅色框作爲文本圖層的背景可見。所以我想在一個紅色的框中間的白色文字。 –

0

我認爲支持你可以爲文本圖層創建一個類別,在類別中你可以爲兩個版本有條件地編碼。 當我們改變圖像時,和我們爲導航欄做的一樣。

,你與不同的IOS版本不同的幀你可以居中你的框架

+0

儘管這是一個有效的答案,並且肯定會起作用,但我希望看到更強大的解決方法。在我的書中,調節是相當ha ish的。謝謝你的努力。 – Byte

0

這在我看來,iOS 6中已考慮到行高(或其他字體相關的功能,影響了實際的垂直繪製位置在繪製CATextLayer的文本內容時字體的字體)。結果是在iOS 6.0中,CATextLayer中具有特定字體的文本不會顯示在CATextLayer幀的頂部邊緣。我發現某些字體有這樣的垂直填充,而其他字體則沒有。在iOS 5.0/5.1中,文本的字形實際顯示在CATextLayer幀的頂部邊緣。

所以我想的一個可能的解決方案可能是將代碼中的textLayer對象從CATextLayer更改爲CALayer(或子類CALayer),並使用Core Text來自定義繪製內容,以便控制所有將在iOS 5.0/5.1和6.0中保持一致。

1

威爾我正在等待一個終極的解決方案,我研究了RTLabel和TTTAttributedLabel,並且做了一個簡單的類,在史蒂夫建議的CALayer上繪製文本。希望它有幫助,請不要猶豫,指出我犯的任何錯誤。

CustomTextLayer.h

#import <QuartzCore/QuartzCore.h> 

@interface CustomTextLayer : CALayer { 
    NSString      *_text; 
    UIColor       *_textColor; 

    NSString      *_font; 
    float       _fontSize; 

    UIColor       *_strokeColor; 
    float       _strokeWidth; 

    CTTextAlignment     _textAlignment; 
    int        _lineBreakMode; 

    float       _suggestHeight; 
} 

-(float) suggestedHeightForWidth:(float) width; 

@property (nonatomic, retain) NSString *text; 
@property (nonatomic, retain) UIColor *textColor; 

@property (nonatomic, retain) NSString *font; 
@property (nonatomic, assign) float fontSize; 

@property (nonatomic, retain) UIColor *strokeColor; 
@property (nonatomic, assign) float strokeWidth; 

@property (nonatomic, assign) CTTextAlignment textAlignment; 

@end 

CustomTextLayer.m

#import <CoreText/CoreText.h> 
#import "CustomTextLayer.h" 

@implementation CustomTextLayer 

@synthesize text = _text, textColor = _textColor; 
@synthesize font = _font, fontSize = _fontSize; 
@synthesize strokeColor = _strokeColor, strokeWidth = _strokeWidth; 
@synthesize textAlignment = _textAlignment; 

-(id) init { 
    if (self = [super init]) { 
     _text = @""; 
     _textColor = [UIColor blackColor]; 

     _font = @"Helvetica"; 
     _fontSize = 12; 

     _strokeColor = [UIColor whiteColor]; 
     _strokeWidth = 0.0; 

     _textAlignment = kCTLeftTextAlignment; 
     _lineBreakMode = kCTLineBreakByWordWrapping; 

    } 
    return self; 
} 

-(void) dealloc { 
    [_text release]; 
    [_textColor release]; 

    [_font release]; 

    [_strokeColor release]; 

    [super dealloc]; 
} 

-(void) setText:(NSString *)text { 
    [_text release]; 
    _text = [text retain]; 
    [self setNeedsDisplay]; 
} 

-(void) setTextColor:(UIColor *)textColor { 
    [_textColor release]; 
    _textColor = [textColor retain]; 
    [self setNeedsDisplay]; 
} 

-(void) setFont:(NSString *)font { 
    [_font release]; 
    _font = [font retain]; 
    [self setNeedsDisplay]; 
} 

-(void) setFontSize:(float)fontSize { 
    _fontSize = fontSize; 
    [self setNeedsDisplay]; 
} 

-(void) setStrokeColor:(UIColor *)strokeColor { 
    [_strokeColor release]; 
    _strokeColor = strokeColor; 
    [self setNeedsDisplay]; 
} 

-(void) setStrokeWidth:(float)strokeWidth { 
    _strokeWidth = 0 ? (strokeWidth < 0) : (-1 * strokeWidth); 
    [self setNeedsDisplay]; 
} 

-(void) setTextAlignment:(CTTextAlignment)textAlignment { 
    _textAlignment = textAlignment; 
    [self setNeedsDisplay]; 
} 

-(void) setFrame:(CGRect)frame { 
    [super setFrame: frame]; 
    [self setNeedsDisplay]; 
} 

-(float) suggestedHeightForWidth:(float) width { 

    CTFontRef fontRef = CTFontCreateWithName((CFStringRef)_font, _fontSize, NULL); 

    CTParagraphStyleSetting paragraphStyles[2] = { 
     {.spec = kCTParagraphStyleSpecifierLineBreakMode, .valueSize = sizeof(CTLineBreakMode), .value = (const void *) &_lineBreakMode}, 
     {.spec = kCTParagraphStyleSpecifierAlignment, .valueSize = sizeof(CTTextAlignment), .value = (const void *) &_textAlignment} 
    }; 
    CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(paragraphStyles, 2); 

    NSDictionary *attrDict = [[NSDictionary alloc] initWithObjectsAndKeys:(id)fontRef, (NSString *)kCTFontAttributeName, (id)_textColor.CGColor, (NSString *)(kCTForegroundColorAttributeName), (id)_strokeColor.CGColor, (NSString *)(kCTStrokeColorAttributeName), (id)[NSNumber numberWithFloat: _strokeWidth], (NSString *)(kCTStrokeWidthAttributeName), (id)paragraphStyle, (NSString *)(kCTParagraphStyleAttributeName), nil]; 

    CFRelease(fontRef); 
    CFRelease(paragraphStyle); 

    NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:_text attributes: attrDict]; 

    // Determine suggested frame height 
    CFRange textRange = CFRangeMake(0, [attrStr length]); 
    CGSize constraint = CGSizeMake(width, 9999); 

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStr); 
    CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, textRange, NULL, constraint, NULL); 
    textSize = CGSizeMake(ceilf(textSize.width), ceilf(textSize.height)); 

    [attrDict release]; 
    [attrStr release]; 

    return textSize.height; 
} 

-(void) renderText:(CGContextRef)ctx { 
    CGContextSetTextMatrix(ctx, CGAffineTransformIdentity); 
    CGContextTranslateCTM(ctx, 0, self.bounds.size.height); 
    CGContextScaleCTM(ctx, 1.0, -1.0); 

    CTFontRef fontRef = CTFontCreateWithName((CFStringRef)_font, _fontSize, NULL); 

    CTParagraphStyleSetting paragraphStyles[2] = { 
     {.spec = kCTParagraphStyleSpecifierLineBreakMode, .valueSize = sizeof(CTLineBreakMode), .value = (const void *) &_lineBreakMode}, 
     {.spec = kCTParagraphStyleSpecifierAlignment, .valueSize = sizeof(CTTextAlignment), .value = (const void *) &_textAlignment} 
    }; 
    CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(paragraphStyles, 2); 

    NSDictionary *attrDict = [[NSDictionary alloc] initWithObjectsAndKeys:(id)fontRef, (NSString *)kCTFontAttributeName, (id)_textColor.CGColor, (NSString *)(kCTForegroundColorAttributeName), (id)_strokeColor.CGColor, (NSString *)(kCTStrokeColorAttributeName), (id)[NSNumber numberWithFloat: _strokeWidth], (NSString *)(kCTStrokeWidthAttributeName), (id)paragraphStyle, (NSString *)(kCTParagraphStyleAttributeName), nil]; 

    CFRelease(fontRef); 
    CFRelease(paragraphStyle); 

    NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:_text attributes: attrDict]; 

    CGMutablePathRef path = CGPathCreateMutable(); 
    CGPathAddRect(path, NULL, self.bounds); 
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStr); 

    CFRange textRange = CFRangeMake(0, [attrStr length]); 
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, textRange, path, NULL); 
    CFArrayRef lines = CTFrameGetLines(frame); 
    NSInteger numberOfLines = CFArrayGetCount(lines); 
    CGPoint lineOrigins[numberOfLines]; 
    CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins); 

    for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) { 
     CGPoint lineOrigin = lineOrigins[lineIndex]; 
     CGContextSetTextPosition(ctx, lineOrigin.x, lineOrigin.y); 
     CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex); 

     if (lineIndex == numberOfLines - 1) { 
      CFRange lastLineRange = CTLineGetStringRange(line); 

      if (!(lastLineRange.length == 0 && lastLineRange.location == 0) && lastLineRange.location + lastLineRange.length < textRange.location + textRange.length) { 
       NSUInteger truncationAttributePosition = lastLineRange.location; 
       CTLineTruncationType truncationType; 
       if (numberOfLines != 1) { 
        truncationType = kCTLineTruncationEnd; 
        truncationAttributePosition += (lastLineRange.length - 1); 
       } 

       NSAttributedString *tokenString = [[NSAttributedString alloc] initWithString:@"\u2026" attributes:attrDict]; 
       CTLineRef truncationToken = CTLineCreateWithAttributedString((CFAttributedStringRef)tokenString); 

       NSMutableAttributedString *truncationString = [[attrStr attributedSubstringFromRange: NSMakeRange(lastLineRange.location, lastLineRange.length)] mutableCopy]; 
       if (lastLineRange.length > 0) { 
        unichar lastCharacter = [[truncationString string] characterAtIndex: lastLineRange.length - 1]; 
        if ([[NSCharacterSet newlineCharacterSet] characterIsMember:lastCharacter]) { 
         [truncationString deleteCharactersInRange:NSMakeRange(lastLineRange.length - 1, 1)]; 
        } 
       } 
       [truncationString appendAttributedString: tokenString]; 
       CTLineRef truncationLine = CTLineCreateWithAttributedString((CFAttributedStringRef) truncationString); 

       CTLineRef truncatedLine = CTLineCreateTruncatedLine(truncationLine, self.bounds.size.width, truncationType, truncationToken); 
       if (!truncatedLine) { 
        // If the line is not as wide as the truncationToken, truncatedLine is NULL 
        truncatedLine = CFRetain(truncationToken); 
       } 

       CTLineDraw(truncatedLine, ctx); 

       CFRelease(truncatedLine); 
       CFRelease(truncationLine); 
       CFRelease(truncationToken); 
      } else { 
       CTLineDraw(line, ctx); 
      } 
     } else { 
      CTLineDraw(line, ctx); 
     } 
    } 

    [attrStr release]; 
    [attrDict release]; 

    CFRelease(path); 
    CFRelease(frame); 
    CFRelease(framesetter); 

} 

-(void) drawInContext:(CGContextRef)ctx { 
    [super drawInContext: ctx]; 
    [self renderText: ctx]; 
} 

@end 
3

t0rst的回答幫助我。 我認爲capHeight和xHeight是關鍵。

CATextLayer *mytextLayer = [CATextLayer layer]; 
    CGFloat fontSize = 30; 
    UIFont *boldFont = [UIFont boldSystemFontOfSize:fontSize]; 
    mytextLayer.font = (__bridge CFTypeRef)(boldFont.fontName); 
    mytextLayer.fontSize = fontSize; 

    CGFloat offsetY = 0; 

    //if system version is grater than 6 
    if(([[[UIDevice currentDevice] systemVersion] compare:@"6" options:NSNumericSearch] == NSOrderedDescending)){ 
     offsetY = -(boldFont.capHeight - boldFont.xHeight); 
    } 

    //you have to set textX, textY, textWidth 
    mytextLayer.frame = CGRectMake(textX, textY + offsetY, textWidth, fontSize);