В iOS 5 и более ранних версиях первая базовая линия в CATextLayer
всегда позиционируется вниз от верхней границы границ путем подъема, полученного из CTLineGetTypographicBounds
при передаче CTLine
, сделанной со строкой для первой строки.
В iOS 6 это больше не относится ко всем шрифтам. Следовательно, когда вы позиционируете CATextLayer
, вы больше не можете надежно решить, куда его поместить, чтобы получить правильное визуальное выравнивание. Или ты можешь? ...
Во-первых, небольшое отступление: некоторое время назад, когда я пытался отработать поведение позиционирования CATextLayer
в iOS 5, я пытался использовать все комбинации высоты крышки, зажима из UIFont и т. д., прежде чем, наконец, обнаружил, что восхождение от CTLineGetTypographicBounds
было тем, что мне было нужно. В процессе я обнаружил, что а) подъем от UIFont ascender
, CTFontGetAscent
и CTLineGetTypographicBounds
непостоянен для определенных шрифтов, и б) подъем часто бывает странным — либо обрезает акценты, либо оставляет слишком много места вверху. Решение а) состоит в том, чтобы знать, какое значение использовать. На самом деле нет решения b), кроме как оставить много места выше, сместив границы CATextLayer
, если, вероятно, у вас будут акценты, которые будут обрезаны.
Вернёмся к iOS 6. Если вы избегаете наиболее оскорбительных шрифтов (начиная с версии 6.0 и, вероятно, они могут быть изменены), вы всё равно можете программно позиционировать CATextLayer
с остальными шрифтами. Нарушителями являются: AcademyEngravedLetPlain, Courier, HoeflerText и Palatino — визуально эти семейства расположены правильно (т.е. без отсечения ) в CATextLayer
, но ни один из трех источников подъема не дает удобного указания на то, где находится базовая линия. Семейства Helvetica и .HelveticaNeueUI (также известные как системные шрифты) правильно располагаются с базовой линией на подъеме, заданном UIFont ascender
, но другие источники подъема не используются.
Некоторые примеры из тестов, которые я сделал. Образец текста рисуется трижды разными цветами. Начало координат находится в левом верхнем углу серого прямоугольника. Черный текст отрисовывается на CTLineDraw
со смещением вниз по восхождению от CTLineGetTypographicBounds
; прозрачный красный рисуется CATextLayer
с границами, равными серому прямоугольнику; прозрачный синий нарисован с добавлением UIKit
NSString
drawAtPoint:withFont:
, расположенным в начале серого поля, и с UIFont
.
1) Хорошо себя зарекомендовавший шрифт Copperplate-Light. Три образца совпадают, что дает темно-бордовый цвет и означает, что всплытия практически одинаковы из всех источников. То же самое для iOS 5 и 6.
2) Курьер под iOS 5. CATextLayer
позиционирует текст слишком высоко (красный), но CTLineDraw
с подъемом от CTLineGetTypographicBounds
(черный) соответствует позиционированию CATextLayer
, поэтому мы можем разместить и исправить оттуда. NSString drawAtPoint:withFont:
(синий) размещает текст без обрезки. (Helvetica и .HelveticaNeueUI ведут себя так же в iOS 6)
3) Курьер под iOS 6. CATextLayer
(красный) теперь размещает текст так, чтобы он не обрезался, но позиционирование больше не соответствует восхождению от CTLineGetTypographicBounds
(черный) или от UIFont
восходящего элемента, используемого в NSString drawAtPoint:withFont:
( синий). Это непригодно для программного позиционирования. (AcademyEngravedLetPlain, HoeflerText и Palatino также ведут себя так же в 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;
}
person
t0rst
schedule
18.10.2012