不需要的垂直填充从iOS 6中的CATextLayer(Unwanted Vertical Padd

2019-07-31 20:30发布

背景:我开始我的项目在iOS 5中,并建立了一个漂亮的按钮,层。 我添加了一个textLayer到按钮并且使用以下代码它中心:

    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(对于那些谁喜欢谷歌地图)。

图片:
这一个是iOS 5及红色是textLayer的背景(使它更加明显)

而这一次的iOS 6


更新:虽然下面我敢肯定,所有的答案都以自己的方式是正确的,我发现t0rst最简单的方法后才能执行此。 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)];

与该代码,我得到一个死点每次。 请注意,这仅与测试HelveticaNeue-Bold上的iOS5和iOS6的。 我不能说别的。

Answer 1:

在IOS 5和之前,在所述第一基线CATextLayer总是从边界由从得到的上升顶向下定位CTLineGetTypographicBounds当通过了CTLine与串用于第一线制成。

在iOS 6中,这并不适用于所有的字体真了。 因此,当你定位CATextLayer你再也不能可靠地决定把它放在哪里得到正确的视觉对准。 或者,你可以吗? ...

首先,顺便说一句:努力工作,出来的时候CATextLayer iOS 5中的定位特性前一阵子,我想终于发现,从提升使用前盖的高度,从UIFont伸等的所有组合CTLineGetTypographicBounds是我需要的人。 在这个过程中,我发现一)从上升UIFont ascenderCTFontGetAscentCTLineGetTypographicBounds对某些字体不一致,和b)的上升经常是奇怪-无论是裁剪的口音或离开的方式上面的空间。 该解决方案的)是知道要使用的值。 是不是真的到B以外的解决方案),比抵消离开上述足够的空间CATextLayer范围,如果它有可能你将有会被切去口音。

返回到iOS 6,如果你避免最坏违规字体(如6.0,并可能随时更改),你仍然可以做的程序化定位CATextLayer与字体的其余部分。 罪犯是:AcademyEngravedLetPlain, 快递 ,和HoeflerText 帕拉提诺 -视觉,这些家庭位置正确地(即,没有削波)在CATextLayer ,但没有的三个上升源给你其中基线被放置的可用指示。 黑体.HelveticaNeueUI(又名系统字体)家庭与给出的上升基线正确位置UIFont ascender ,但其他上升源使用的不是。

从测试的一些例子我做到了。 示例文本是用不同的颜色绘制三次。 坐标原点是灰色框的左上角。 黑白文本被绘制CTLineDraw向下偏移从上升CTLineGetTypographicBounds ; 透明的红色是通过绘制CATextLayer与边界等于灰色方块; 透明的蓝色被吸入与UIKit NSString此外drawAtPoint:withFont:在灰色方块的起源,并与定位UIFont

1)一种表现良好的字体,铜版光强。 这三个样本是一致的,给人栗色,并且这意味着攀登足够近的来自所有来源相同。 相同的iOS 5和6。

2)下的iOS 5, 快递 CATextLayer位置文本太高(红色),但CTLineDraw从上升CTLineGetTypographicBounds (黑色)匹配CATextLayer定位-所以我们可以把从那里正确。 NSString drawAtPoint:withFont:蓝色)放置文本而不削波。 ( 黑体.HelveticaNeueUI这样的表现在IOS 6)

3)的iOS 6下快递 CATextLayer (红色)现在放置文本,以便它不会被截断,但定位不再与从上升CTLineGetTypographicBounds (黑色)或UIFont中使用上升器NSString drawAtPoint:withFont:蓝色)。 这是不能用于程序化的定位。 (AcademyEngravedLetPlain,HoeflerText帕拉提诺也这样的表现在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;
}


Answer 2:

t0rst的回答可以帮助我。 我想,大写高度和X字高是关键。

    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);


Answer 3:

威乐我在等待最终的解决方案,我研究过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


Answer 4:

我认为,以支持您可以创建文本层的类别,类别可以为这两个版本的代码有条件它。 同我们为导航栏做当我们改变形象。

当你与不同的IOS版本不同的帧你可以居中你的框架



Answer 5:

这在我看来,iOS 6中已考虑到行高(或其他字体相关的功能影响字形的实际垂直绘制位置)绘制CATextLayer的文本内容时的字体。 其结果是,在IOS 6.0,在CATextLayer与某种字体的文本没有在CATextLayer的框架的顶部边缘显示。 我发现,有些字体有这样的垂直填充,而有的则没有。 而在IOS 5.0 / 5.1,该文本的字形实际上在CATextLayer的框架的顶部边缘显示。

所以,一个可能的解决方案我想可能是改变textLayer对象在你的代码CATextLayer只是CALayer的(或子类的CALayer),并使用核心文本自定义得出这样的内容,你能控制一切,将跨越一致的iOS 5.0 / 5.1和6.0。



文章来源: Unwanted Vertical Padding from iOS 6 on CATextLayer