16. Core Text
• Оптимальное сложность/возможности
• CTFontRef
• CTFrame, CTLine, CTRun, CTGlyph
• Можно использовать в нескольких
потоках, но с ограничениями...
16
17. Core Text
Потокобезопасно Непотокобезопасно
• Все функции • Объекты, задающие
размещение:
• Объекты, не задающие
размещение текста: - CTFrameSetter
- CTFont - CTTypeSetter
- CTFontDescriptor - CTFrame
- ... - CTRun
- CTLine
- ...
17
19. Core Text
• CTFrame – область вывода всего текста
• CTLine – одна строка
• CTRun – последовательность символов с
одинаковыми атрибутами
• CTGlyph – один символ шрифта
19
20. Core Text
This is CTRun Next CTRun The End.
CTLine
CTFrame
20
21. Core Text
• Отраженная и сдвинутая по вертикали
система координат
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// Делаем текущую матрицу единичной
CGContextSetTextMatrix( context, CGAffineTransformIdentity );
// Добавляем к матрице перенос по вертикали
CGContextTranslateCTM( context, 0, rect.size.height );
// Добавляем отражение по вертикали
CGContextScaleCTM( context, 1, -1 );
// ...
21
22. Core Text
• Создаем CTFrameSetter из CFAttributedString
CTFramesetterRef frameSetter =
CTFramesetterCreateWithAttributedString( attrStr );
• Далее получаем CTFrame, используя CGPath
CGFloat width = rect.size.width,
height = rect.size.height;
CGFloat rect = CGRectMake(0, 0, width, height);
CGMutablePathRef mutablePath = CGPathCreateMutable();
CGPathAddRect( mutablePath, NULL, rect );
CTFrameRef frame = CTFramesetterCreateFrame(
frameSetter, frameRange, mutablePath, NULL );
22
23. Core Text
• Создаем CTFrameSetter из CFAttributedString
CTFramesetterRef frameSetter =
CTFramesetterCreateWithAttributedString( attrStr );
• Далее получаем CTFrame, используя CGPath
CGFloat width = rect.size.width,
height = rect.size.height;
CGFloat rect = CGRectMake(0, 0, width, height);
CGMutablePathRef mutablePath = CGPathCreateMutable();
CGPathAddRect( mutablePath, NULL, rect );
CTFrameRef frame = CTFramesetterCreateFrame(
frameSetter, frameRange, mutablePath, NULL );
23
24. Core Text
• Полученный CTFrame является моделью, по
которой отрисовывается текст в
графическом контексте после вызова
CTFrameDraw( frame, context );
• Аналогичные функции для отрисовки
отдельных CTLine, CTRun
CGContextSetTextPosition(context, p.x, p.y);
CTLineDraw(line, context);
CTRunDraw(run, context, CFRangeMake(0, 0));
24
25. Core Text
• Полученный CTFrame является моделью, по
которой отрисовывается текст в
графическом контексте после вызова
CTFrameDraw( frame, context );
• Аналогичные функции для отрисовки
отдельных CTLine, CTRun
CGContextSetTextPosition(context, p.x, p.y);
CTLineDraw(line, context);
CTRunDraw(run, context, CFRangeMake(0, 0));
25
26. Core Text
• Полученный CTFrame является моделью, по
которой отрисовывается текст в
графическом контексте после вызова
CTFrameDraw( frame, context );
• Аналогичные функции для отрисовки
отдельных CTLine, CTRun
CGContextSetTextPosition(context, p.x, p.y);
CTLineDraw(line, context);
CTRunDraw(run, context, CFRangeMake(0, 0));
26
27. Core Text
• Проход по всем участкам фрейма:
NSArray* lines = (__bridge NSArray *)CTFrameGetLines( frame );
for ( int lineIndex = 0; lineIndex < length; ++lineIndex ) {
CTLineRef line =
(__bridge CTLineRef)[lines objectAtIndex: lineIndex];
NSArray* runs =
(__bridge NSArray *)CTLineGetGlyphRuns( line );
for ( int runIndex = 0; runIndex < runs.count; ++runIndex ) {
CTRunRef run =
(__bridge CTRunRef)[runs objectAtIndex: runIndex];
// ...
}
}
27
28. Core Text
• Проход по всем участкам фрейма:
NSArray* lines = (__bridge NSArray *)CTFrameGetLines( frame );
for ( int lineIndex = 0; lineIndex < length; ++lineIndex ) {
CTLineRef line =
(__bridge CTLineRef)[lines objectAtIndex: lineIndex];
NSArray* runs =
(__bridge NSArray *)CTLineGetGlyphRuns( line );
for ( int runIndex = 0; runIndex < runs.count; ++runIndex ) {
CTRunRef run =
(__bridge CTRunRef)[runs objectAtIndex: runIndex];
// ...
}
}
28
29. Core Text
• Проход по всем участкам фрейма:
NSArray* lines = (__bridge NSArray *)CTFrameGetLines( frame );
for ( int lineIndex = 0; lineIndex < length; ++lineIndex ) {
CTLineRef line =
(__bridge CTLineRef)[lines objectAtIndex: lineIndex];
NSArray* runs =
(__bridge NSArray *)CTLineGetGlyphRuns( line );
for ( int runIndex = 0; runIndex < runs.count; ++runIndex ) {
CTRunRef run =
(__bridge CTRunRef)[runs objectAtIndex: runIndex];
// ...
}
}
29
30. Core Text
• Проход по всем участкам фрейма:
NSArray* lines = (__bridge NSArray *)CTFrameGetLines( frame );
for ( int lineIndex = 0; lineIndex < length; ++lineIndex ) {
CTLineRef line =
(__bridge CTLineRef)[lines objectAtIndex: lineIndex];
NSArray* runs =
(__bridge NSArray *)CTLineGetGlyphRuns( line );
for ( int runIndex = 0; runIndex < runs.count; ++runIndex ) {
CTRunRef run =
(__bridge CTRunRef)[runs objectAtIndex: runIndex];
// ...
}
}
30
31. Core Text
y origin + ascent
origin При определенных
origin – descent условиях вещество
трансформирует межядерный
фонон
x
31
39. Core Text
• Установка свойств параграфов доступна:
- iOS < 6: ТОЛЬКО при инициализации
NSAttributedString / NSMutableAttributedString
- iOS 6: можно делать addAttribute у
NSMutableAttributedString
39
40. Core Text
• Неприятности:
- выделения пишем сами
- анализ текста (html, rtf и чего угодно
своего, например, с разбивкой на
параграфы) тоже пишем сами
40
41. Core Text
• Приятности:
✓ огромные возможности по
кастомизации вывода текста
✓ очень удобно и быстро, когда нужно
использовать UILabel, но цветной или
с маркированными участками
✓ Достаточно быстр
41
42. Core Text
• Тест на скорость:
‣ UITableView, 5000 ячеек
‣ В каждой ячейке label с кастомным
шрифтом MetaPro-Normal, цвет текста с
прозрачностью
‣ iPhone 4, Xcode Instruments -> CA Instrument
42
43. Core Text
• Тест на скорость:
‣ UITableView, 5000 ячеек
‣ В каждой ячейке label с кастомным
шрифтом MetaPro-Normal, цвет текста с
прозрачностью
‣ iPhone 4, Xcode Instruments -> CA Instrument
UILabel Core Text
~ 55–56 fps ~ 60 fps 43
44. Core Text
• Тест на скорость:
‣ UITableView, 5000 ячеек
‣ В каждой ячейке label с кастомным
шрифтом MetaPro-Normal, цвет текста с
прозрачностью
‣ iPhone 4, Xcode Instruments -> CA Instrument
UILabelCore Text точно неCore Text
~ 55–56 fpsмедленнее ~ 60 fps 44
48. Маркировка
• Создаём наследник UIView:
- selectWithPattern:(NSString *)pattern - метод,
который будет создавать модель для
маркировки указанного текста
- drawRect:(CGRect)rect - здесь будет
непосредственная отрисовка текста с
маркировкой
48
49. Маркировка
• Добавляем какой-нибудь атрибут к
участкам, которые хотим выделить и
сохраняем их в в self.ranges
[regexp enumerateMatchesInString: ...
usingBlock:^(NSTextCheckingResult *result, ...) {
// При совпадении куска текста с шаблоном:
// 1. Добавляем участок в список совпадений
[ranges addObject: [NSValue valueWithRange: [result range]]];
// 2. Помечаем участок цветом в отдельной атрибутированной строке
[attrTextMutable addAttribute: kCTForegroundColorAttributeName
value: [UIColor redColor].CGColor
range: [result range]];
}]; 49
50. Маркировка
• Добавляем какой-нибудь атрибут к
участкам, которые хотим выделить, и
сохраняем их в в self.ranges
[regexp enumerateMatchesInString: ...
usingBlock:^(NSTextCheckingResult *result, ...) {
// При совпадении куска текста с шаблоном:
// 1. Добавляем участок в список совпадений
[ranges addObject: [NSValue valueWithRange: [result range]]];
// 2. Помечаем участок цветом в отдельной атрибутированной строке
[attrTextMutable addAttribute: kCTForegroundColorAttributeName
value: [UIColor redColor].CGColor
range: [result range]];
}]; 50
51. Маркировка
• В drawRect проходим по всем CTRun
каждой строки и получаем их frame, если
они входят в сохраненные участки
➡ При проверке подходит ли run
обращаем внимание на то, что конец
строки может разорвать CTRun => нужно
проверять включение, а не совпадение
51
52. Маркировка
• Маркированным участкам теперь
соответствуют отдельные CTRun
CFRange runCFRange = CTRunGetStringRange( run );
NSValue* parentRangeValue = [self parentForRange: runRange];
if ( parentRangeValue ) {
// CTRun подходит, можно получить его frame и использовать это
...
}
• Получаем frame рана с помощью
CTRunGetTypographicBounds
• Осталось только что-нибудь добавить
для выделения.... 52
53. Маркировка
• Добавим кнопку, frame которой будет
выглядеть следующим образом:
CGRect buttonFrame = CGRectMake(
leftX - leftPadding,
topY - leftPadding,
runWidth + leftPadding + rightPadding,
runHeight + bottomPadding + topPadding );
• Не забываем, что мы работали в
преобразованной системе координат:
CGAffineTransform transform =
CGAffineTransformMakeTranslation(0, rect.size.height);
transform = CGAffineTransformScale(1, -1);
buttonFrame = CGRectApplyAffineTransform(transform, buttonFrame); 53
54. Маркировка
Можно использовать нажатие
кнопки для чего-нибудь
54
56. Два столбца
• Два столбца делаются просто - выводим
текст в нескольких CTFrame
- задаем CGPath для границ столбца
CGRect colRect = CGRectMake(originX, originY, colWidth, colHeight);
CGPathAddRect(rectPath, NULL, colRect);
- чтобы определить, сколько текста
вывели в предыдущем столбце,
используем:
CFRange prevColTextRange =
CTFrameGetVisibleStringRange(colFrames[columnIdx - 1]);
56
57. Два столбца
• Два столбца делаются просто - выводим
текст в нескольких CTFrame
- задаем CGPath для границ столбца
CGRect colRect = CGRectMake(originX, originY, colWidth, colHeight);
CGPathAddRect(rectPath, NULL, colRect);
- чтобы определить, сколько текста
вывели в предыдущем столбце,
используем:
CFRange prevColTextRange =
CTFrameGetVisibleStringRange(colFrames[columnIdx - 1]);
57
59. Обтекание
• Чтобы текст обтекал shape, нужно
вырезать последний из основного
фрейма
- Не нужно пытаться делать это через
Core Graphics
- В Core Text есть специальные
опции, которые задают вырезаемые
области
59
60. Обтекание
• Каждый вырезаемый CGPath задается
через CFDictionaryRef
- (CFDictionaryRef)createClippingPathWithPath:(CGPathRef)pathRef {
NSDictionary* ret =
[NSDictionary dictionaryWithObject: (__bridge id)(pathRef)
forKey: kCTFramePathClippingPathAttributeName];
return (__bridge CFDictionaryRef)(ret);
}
60
61. Обтекание
• Затем массив из CDictionaryRef ставится
значением опции и передается в
CTFrameSetterCreateFrame
frameOptionsDict =
[NSDictionary dictionaryWithObject: clippingPaths
forKey: kCTFrameClippingPathsAttributeName];
CTFramesetterCreateFrame(...,
(__bridge CFDictionaryRef)frameOptionsDict);
61
62. Apple Font Tool Suite
https://developer.apple.com/fonts/
ftxdumperfuser -t hhea -A d Font.otf
Custom font Helvetica Neue
ascender="716" ascender="975"
descender="-212" descender="-217"
lineGap="219" lineGap="29"
ftxdumperfuser -t hhea -A f Font.otf
62