后台:我在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
更新:虽然我确定以下所有答案都是以自己的方式正确的,但我发现帖子是第一个最简单的方法来执行此操作。 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
进行测试。我不能说其他任何事情。
答案 0 :(得分:8)
在iOS 5及之前的版本中,CATextLayer
中的第一个基线始终位于从CTLineGetTypographicBounds
获得的上升时从边界顶部向下定位的CTLine
第一行的字符串。
在iOS 6中,这不再适用于所有字体。因此,当您定位CATextLayer
时,您无法再可靠地决定将其放置在何处以获得正确的视觉对齐。或者你呢? ...
首先,暂且不说:在iOS 5中尝试计算CATextLayer
的定位行为时,我尝试使用帽子高度的所有组合,来自UIFont等的ascender,然后最终发现上升CTLineGetTypographicBounds
是我需要的那个。在此过程中,我发现a)来自UIFont ascender
,CTFontGetAscent
和CTLineGetTypographicBounds
的上升对于某些字体是不一致的,并且b)上升经常是奇怪的 - 要么裁剪重音或离开通往上方的空间。 a)的解决方案是知道要使用哪个值。 b)除了通过偏移CATextLayer
边界而留下足够的空间,如果它可能会有被剪裁的重音符号,则没有真正的解决方案。
返回iOS 6.如果您避免使用最严重的违规字体(从6.0开始,可能会有变化),您仍然可以使用其他字体对CATextLayer
进行编程定位。违规者是: AcademyEngravedLetPlain , Courier , HoeflerText 和 Palatino - 在视觉上,这些家庭的位置正确(即没有剪裁) )在CATextLayer
中,但三个上升源中没有一个为您提供基线放置位置的可用指示。 Helvetica 和 .HelveticaNeueUI (又称系统字体)系列在UIFont ascender
给出的上升时正确定位基线,但其他上升源无法使用。< / p>
我做过的测试的一些例子。示例文本以不同颜色绘制三次。坐标原点位于灰色框的左上角。 CTLineDraw
的黑色文字由CTLineGetTypographicBounds
的上升向下偏移;透明红色由CATextLayer
绘制,边界等于灰色框;透明蓝色是使用UIKit
NSString
加法drawAtPoint:withFont:
绘制的,位于灰色框的原点和UIFont
。
1)表现良好的字体,Copperplate-Light。这三个样本是重合的,给出了栗色,并且意味着所有来源的上升距离足够近。 iOS 5和6也是如此。
iOS 5下的 Courier 。
CATextLayer
文字位置太高(红色),但CTLineDraw
上升CTLineGetTypographicBounds
(黑色)匹配{{ 1}}定位 - 所以我们可以从那里放置和纠正。 CATextLayer
(蓝色)放置文本而不剪裁。 ( Helvetica 和 .HelveticaNeueUI 在iOS 6中的行为如下)
iOS 6下的 Courier 。
NSString drawAtPoint:withFont:
(红色)现在放置文本,使其不会被剪裁,但定位不再与CATextLayer
的上升匹配(黑色)或来自CTLineGetTypographicBounds
(蓝色)中使用的UIFont
上升者。这对于编程定位是不可用的。 ( AcademyEngravedLetPlain , HoeflerText 和 Palatino 在iOS 6中的行为也是如此)
希望这有助于避免我经历的浪费时间的一些时间,如果你想深入了解一下,可以玩一下:
NSString drawAtPoint:withFont:
答案 1 :(得分: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);
答案 2 :(得分:1)
Wile我正在等待一个终极解决方案,我研究了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 :(得分:0)
我认为支持你可以为文本图层创建一个类别,在类别中你可以为两个版本有条件地编码。 改变图像的时候和导航条一样。
您可以像对不同ios版本的不同框架一样使框架居中
答案 4 :(得分: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中保持一致。