UILabel по умолчанию кернинг отличается от CATextLayer
У меня есть UILabel
со строкой 'LA'. У меня также есть CATextLayer
с теми же символами в NSAttributedString
, назначенным его свойству string
. Кернинг в UILabel
заметно отличается от CATextLayer
. Вот код.
- (void)viewDidLoad
{
[super viewDidLoad];
//
// UILabel
//
UILabel *label1 = [[UILabel alloc] initWithFrame:CGRectMake(20, 50, 280, 100)];
label1.text = @"LA";
label1.backgroundColor = [UIColor clearColor];
label1.font = [UIFont fontWithName:@"Futura" size:90.0];
[self.view addSubview:label1];
//
// CATextLayer
//
UILabel *label2 = [[UILabel alloc] initWithFrame:CGRectMake(20, 130, 280, 100)];
label2.backgroundColor = [UIColor clearColor];
CATextLayer *textLayer = [[CATextLayer alloc] init];
textLayer.frame = label2.layer.bounds;
textLayer.contentsScale = [[UIScreen mainScreen] scale];
[label2.layer addSublayer:textLayer];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:@"LA"];
CTFontRef aFont = CTFontCreateWithName((__bridge CFStringRef)@"Futura", 90.0, NULL);
[string addAttribute:(NSString*)kCTFontAttributeName value:(__bridge id)aFont range:NSMakeRange(0, [string length])];
textLayer.string = string;
[self.view addSubview:label2];
}
Здесь изображение результатов.
Почему кернинг отличается между этими двумя методами и что я делаю неправильно в примере CATextLayer
?
Ответы
Ответ 1
UIKit обычно использует WebKit для его текстового рендеринга (как видно в этом журнале сбоев), скорее всего, по соображениям производительности. Если вам действительно нужна сверхточная точность, то есть пользовательские UILabel
reimplementations, используя CoreText
в качестве внутреннего.
EDIT:
Начиная с iOS7 это уже не так, поскольку UILabel
использует TextKit для его рендеринга, который также основан на CoreText.
Ответ 2
вы должны добавить атрибут к вашей NSMutableAttributedString.
Для кернинга:
CGFloat characterspacing = 10.0f;
CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&characterspacing);
[string addAttribute:(id)kCTKernAttributeName value:(id)num range:NSMakeRange(0 , [string length])];
CFRelease(num);
Если вам также нужен интервал между строками или установить LineBreadMode:
CTLineBreakMode linebreak = kCTLineBreakByCharWrapping;
CTParagraphStyleSetting linebreakStyle;
linebreakStyle.spec = kCTParagraphStyleSpecifierLineBreakMode;
linebreakStyle.valueSize = sizeof(linebreak);
linebreakStyle.value = &linebreak;
CTParagraphStyleSetting lineSpaceStyle;
CGFloat linespacing = self.linesSpacing;
lineSpaceStyle.spec = kCTParagraphStyleSpecifierLineSpacingAdjustment;
lineSpaceStyle.valueSize = sizeof(linespacing);
lineSpaceStyle.value =&linespacing;
CTParagraphStyleSetting settings[ ] ={linebreakStyle,lineSpaceStyle};
CTParagraphStyleRef style = CTParagraphStyleCreate(settings ,2);
[string addAttribute:(id)kCTParagraphStyleAttributeName value:(id)style range:NSMakeRange(0 , [string length])];
CFRelease(style);
В конце концов, вам нужно рассчитать количество строк (linenum) о вашем кернинге, межстрочном интервалах и LineBreakMode:
CTFramesetterRef myframesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string);
CGMutablePathRef leftColumnPath = CGPathCreateMutable();
CGPathAddRect(leftColumnPath, NULL ,CGRectMake(0 , 0 , Lable.frame.size.width, MAXFLOAT));
CTFrameRef leftFrame = CTFramesetterCreateFrame(myframesetter,CFRangeMake(0, 0), leftColumnPath , NULL);
CFArrayRef lines = CTFrameGetLines(leftFrame);
linenum = (int)CFArrayGetCount(lines);
CFRelease(myframesetter);
CFRelease(leftFrame);
CGPathRelease(leftColumnPath);
Ответ 3
Хорошо Core Text
действительно отличается по сравнению с чертежными строками с использованием UIKit, вероятно, потому, что оно происходит от Core Foundation
, а не AppKit
или UIKit
. Я понимаю ваши требования, чтобы использовать метку для выполнения сложной работы метрики в строке. Единственное решение для меня - сопоставить кернинг UILabel
в атрибутной строке, к сожалению, я не знаю точного значения, но вы можете использовать это свойство, чтобы изменить это значение kCTKernAttributeName
. Вы также должны обратить внимание на интерлайн, который может быть не таким.
Принуждая это значение к соответствию кернингам, вы можете иметь правильное поведение. Если вы хотите противоположное (соответствие CT kerning), вы должны выполнить некоторую математику, а затем применить к метке a UIEdgeInset
для вычисления правильной метки.
Надеюсь, это поможет.