Подтвердить что ты не робот

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?

4b9b3361

Ответ 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 для вычисления правильной метки.
Надеюсь, это поможет.