Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Core Text: раскрываем интерактивные возможности текста

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all
  • Be the first to comment

Core Text: раскрываем интерактивные возможности текста

  1. 1. Core Textgithub.com/anyvoid/CoreTextDemo
  2. 2. Константин КиселёвВедущий разработчик
  3. 3. Содержание๏Основы Core Text• Маркируем области в тексте• Два столбца• Обтекание текстом 3
  4. 4. Рецепты Юлии Высоцкой 4
  5. 5. Рецепты Юлии Высоцкой При нажатии на ссылку со временем открывается установка таймера 5
  6. 6. Рецепты Юлии Высоцкой 6
  7. 7. Бар Депозит – Управление Баром Во время набора имени в поиске участки совпадения подсвечиваются в выпадающем меню 7
  8. 8. Рендеринг текста UIKit Core Text Quartz Core 8
  9. 9. UIKit• Ничего сложного• UIFont• UILabel, UITextView, UIWebView• Минимальные возможности настройки отображения• в iOS 6 можно задавать NSAttributedString ! 9
  10. 10. Рендеринг текста UIKit Core Text Quartz Core 10
  11. 11. Quartz Core• Непосредственная отрисовка глифов• CGFontRef• Фиксированная информация о шрифте• Использование — корректировка отображения 11
  12. 12. Quartz CoreCGDataProviderRef fontDataProvider = CGDataProviderCreateWithFilename( [fontPath UTF8String] );CGFontRef font = CGFontCreateWithDataProvider( fontDataProvider );CGGlyph glyphs[glyphCount];for ( size_t i = 0; i < glyphCount; ++i ) { ... glyphs[i] = CGFontGetGlyphWithGlyphName( font, glyphName ); ...}CGContextSetFont( context, font );CGContextSetFontSize( context, fontSize );CGContextShowGlyphsAtPoint( context, p.x, p.y, glyphs, glyphCount ); 12
  13. 13. Quartz CoreCGDataProviderRef fontDataProvider = CGDataProviderCreateWithFilename( [fontPath UTF8String] );CGFontRef font = CGFontCreateWithDataProvider( fontDataProvider );CGGlyph glyphs[glyphCount];for ( size_t i = 0; i < glyphCount; ++i ) { ... glyphs[i] = CGFontGetGlyphWithGlyphName( font, glyphName ); ...}CGContextSetFont( context, font );CGContextSetFontSize( context, fontSize );CGContextShowGlyphsAtPoint( context, p.x, p.y, glyphs, glyphCount ); 13
  14. 14. Quartz CoreCGDataProviderRef fontDataProvider = CGDataProviderCreateWithFilename( [fontPath UTF8String] );CGFontRef font = CGFontCreateWithDataProvider( fontDataProvider );CGGlyph glyphs[glyphCount];for ( size_t i = 0; i < glyphCount; ++i ) { ... glyphs[i] = CGFontGetGlyphWithGlyphName( font, glyphName ); ...}CGContextSetFont( context, font );CGContextSetFontSize( context, fontSize );CGContextShowGlyphsAtPoint( context, p.x, p.y, glyphs, glyphCount ); 14
  15. 15. Рендеринг текста UIKit Core Text Quartz Core 15
  16. 16. Core Text• Оптимальное сложность/возможности• CTFontRef• CTFrame, CTLine, CTRun, CTGlyph• Можно использовать в нескольких потоках, но с ограничениями... 16
  17. 17. Core TextПотокобезопасно Непотокобезопасно• Все функции • Объекты, задающие размещение:• Объекты, не задающие размещение текста: - CTFrameSetter - CTFont - CTTypeSetter - CTFontDescriptor - CTFrame - ... - CTRun - CTLine - ... 17
  18. 18. Core Text CGPath CTLine CTRun CTGlyph CTGlyphСFAttributedString CTFrameSetter CTRun CTLine CTTypeSetter CTFrame 18
  19. 19. Core Text• CTFrame – область вывода всего текста• CTLine – одна строка• CTRun – последовательность символов с одинаковыми атрибутами• CTGlyph – один символ шрифта 19
  20. 20. Core Text This is CTRun Next CTRun The End. CTLine CTFrame 20
  21. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 31. Core Texty origin + ascent origin При определенныхorigin – descent условиях вещество трансформирует межядерный фонон x 31
  32. 32. Core Text• Получаем origin-ы для строк: CGPoint lineOrigins[lines.count]; CTFrameGetLineOrigins( frame, CFRangeMake(0, lines.count), lineOrigins);• Получаем frame строки: lineWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &leading); lineHeight = ascent + descent + leading;• Получаем frame для CTRun: runWidth = CTRunGetTypographicBounds( run, CFRangeMake(0, 0), &runAscent, &runDescent, &runLeading ); topY = lineOrigins[lineIndex].y + ascent; leftX = CTLineGetOffsetForStringIndex(line, runRange.location, NULL); runHeight = runAscent + runDescent + runLeading; 32
  33. 33. Core Text• Получаем origin-ы для строк: CGPoint lineOrigins[lines.count]; CTFrameGetLineOrigins( frame, CFRangeMake(0, lines.count), lineOrigins);• Получаем frame строки: lineWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &leading); lineHeight = ascent + descent + leading;• Получаем frame для CTRun: runWidth = CTRunGetTypographicBounds( run, CFRangeMake(0, 0), &runAscent, &runDescent, &runLeading ); topY = lineOrigins[lineIndex].y + ascent; leftX = CTLineGetOffsetForStringIndex(line, runRange.location, NULL); runHeight = runAscent + runDescent + runLeading; 33
  34. 34. Core Text• Получаем origin-ы для строк: CGPoint lineOrigins[lines.count]; CTFrameGetLineOrigins( frame, CFRangeMake(0, lines.count), lineOrigins);• Получаем frame строки: lineWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &leading); lineHeight = ascent + descent + leading;• Получаем frame для CTRun: runWidth = CTRunGetTypographicBounds( run, CFRangeMake(0, 0), &runAscent, &runDescent, &runLeading ); topY = lineOrigins[lineIndex].y + ascent; leftX = CTLineGetOffsetForStringIndex(line, runRange.location, NULL); runHeight = runAscent + runDescent + runLeading; 34
  35. 35. Core Text• Получаем origin-ы для строк: CGPoint lineOrigins[lines.count]; CTFrameGetLineOrigins( frame, CFRangeMake(0, lines.count), lineOrigins);• Получаем frame строки: lineWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &leading); lineHeight = ascent + descent + leading;• Получаем frame для CTRun: runWidth = CTRunGetTypographicBounds( run, CFRangeMake(0, 0), &runAscent, &runDescent, &runLeading ); topY = lineOrigins[lineIndex].y + ascent; leftX = CTLineGetOffsetForStringIndex(line, runRange.location, NULL); runHeight = runAscent + runDescent + runLeading; 35
  36. 36. Core Text• Получаем origin-ы для строк: CGPoint lineOrigins[lines.count]; CTFrameGetLineOrigins( frame, CFRangeMake(0, lines.count), lineOrigins);• Получаем frame строки: lineWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &leading); lineHeight = ascent + descent + leading;• Получаем frame для CTRun: runWidth = CTRunGetTypographicBounds( run, CFRangeMake(0, 0), &runAscent, &runDescent, &runLeading ); topY = lineOrigins[lineIndex].y + ascent; leftX = CTLineGetOffsetForStringIndex(line, runRange.location, NULL); runHeight = runAscent + runDescent + runLeading; 36
  37. 37. Core Text• Полезные свойства параграфов: • межстрочный интервал • выравнивание по горизонтали • line break mode • отступ красной строки • ... 37
  38. 38. Core Text• CTParagraphStyleSettingCTParagraphStyleSetting parSet[] = { {kCTParagraphStyleSpecifierAlignment, sizeof(value), &value}, {kCTParagraphStyleSpecifierLineSpacing, ...}, {kCTParagraphStyleSpecifierLineBreakMode, ...} ...};• CTParagraphStyleRefNSInteger optsNum = sizeof(parSet) / sizeof(CTParagraphStyleSetting);CTParagraphStyleRef parStyle = CTParagraphStyleCreate( parSet, optsNum); 38
  39. 39. Core Text• Установка свойств параграфов доступна: - iOS < 6: ТОЛЬКО при инициализации NSAttributedString / NSMutableAttributedString - iOS 6: можно делать addAttribute у NSMutableAttributedString 39
  40. 40. Core Text• Неприятности: - выделения пишем сами - анализ текста (html, rtf и чего угодно своего, например, с разбивкой на параграфы) тоже пишем сами 40
  41. 41. Core Text• Приятности: ✓ огромные возможности по кастомизации вывода текста ✓ очень удобно и быстро, когда нужно использовать UILabel, но цветной или с маркированными участками ✓ Достаточно быстр 41
  42. 42. Core Text• Тест на скорость: ‣ UITableView, 5000 ячеек ‣ В каждой ячейке label с кастомным шрифтом MetaPro-Normal, цвет текста с прозрачностью ‣ iPhone 4, Xcode Instruments -> CA Instrument 42
  43. 43. Core Text• Тест на скорость: ‣ UITableView, 5000 ячеек ‣ В каждой ячейке label с кастомным шрифтом MetaPro-Normal, цвет текста с прозрачностью ‣ iPhone 4, Xcode Instruments -> CA Instrument UILabel Core Text~ 55–56 fps ~ 60 fps 43
  44. 44. Core Text• Тест на скорость: ‣ UITableView, 5000 ячеек ‣ В каждой ячейке label с кастомным шрифтом MetaPro-Normal, цвет текста с прозрачностью ‣ iPhone 4, Xcode Instruments -> CA Instrument UILabelCore Text точно неCore Text~ 55–56 fpsмедленнее ~ 60 fps 44
  45. 45. Содержание• Основы Core Text๏Маркируем области в тексте• Два столбца• Обтекание текстом 45
  46. 46. Маркировка• Результаты поиска• Кастомные ссылки в UILabel http://anyvoid.ru➡И все это без UIWebView ! 46
  47. 47. Маркировка 47
  48. 48. Маркировка• Создаём наследник UIView: - selectWithPattern:(NSString *)pattern - метод, который будет создавать модель для маркировки указанного текста - drawRect:(CGRect)rect - здесь будет непосредственная отрисовка текста с маркировкой 48
  49. 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. 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. 51. Маркировка• В drawRect проходим по всем CTRun каждой строки и получаем их frame, если они входят в сохраненные участки➡ При проверке подходит ли run обращаем внимание на то, что конец строки может разорвать CTRun => нужно проверять включение, а не совпадение 51
  52. 52. Маркировка• Маркированным участкам теперь соответствуют отдельные CTRun CFRange runCFRange = CTRunGetStringRange( run ); NSValue* parentRangeValue = [self parentForRange: runRange]; if ( parentRangeValue ) { // CTRun подходит, можно получить его frame и использовать это ... }• Получаем frame рана с помощью CTRunGetTypographicBounds• Осталось только что-нибудь добавить для выделения.... 52
  53. 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. Маркировка Можно использовать нажатие кнопки для чего-нибудь 54
  55. 55. Содержание• Основы Core Text• Маркируем области в тексте๏Два столбца• Обтекание текстом 55
  56. 56. Два столбца• Два столбца делаются просто - выводим текст в нескольких CTFrame - задаем CGPath для границ столбцаCGRect colRect = CGRectMake(originX, originY, colWidth, colHeight);CGPathAddRect(rectPath, NULL, colRect); - чтобы определить, сколько текста вывели в предыдущем столбце, используем:CFRange prevColTextRange = CTFrameGetVisibleStringRange(colFrames[columnIdx - 1]); 56
  57. 57. Два столбца• Два столбца делаются просто - выводим текст в нескольких CTFrame - задаем CGPath для границ столбцаCGRect colRect = CGRectMake(originX, originY, colWidth, colHeight);CGPathAddRect(rectPath, NULL, colRect); - чтобы определить, сколько текста вывели в предыдущем столбце, используем:CFRange prevColTextRange = CTFrameGetVisibleStringRange(colFrames[columnIdx - 1]); 57
  58. 58. Содержание• Основы Core Text• Выделяем области в тексте• Два столбца๏Обтекание текстом 58
  59. 59. Обтекание• Чтобы текст обтекал shape, нужно вырезать последний из основного фрейма - Не нужно пытаться делать это через Core Graphics - В Core Text есть специальные опции, которые задают вырезаемые области 59
  60. 60. Обтекание • Каждый вырезаемый CGPath задается через CFDictionaryRef- (CFDictionaryRef)createClippingPathWithPath:(CGPathRef)pathRef { NSDictionary* ret = [NSDictionary dictionaryWithObject: (__bridge id)(pathRef) forKey: kCTFramePathClippingPathAttributeName]; return (__bridge CFDictionaryRef)(ret);} 60
  61. 61. Обтекание • Затем массив из CDictionaryRef ставится значением опции и передается в CTFrameSetterCreateFrameframeOptionsDict = [NSDictionary dictionaryWithObject: clippingPaths forKey: kCTFrameClippingPathsAttributeName];CTFramesetterCreateFrame(..., (__bridge CFDictionaryRef)frameOptionsDict); 61
  62. 62. Apple Font Tool Suite https://developer.apple.com/fonts/ftxdumperfuser -t hhea -A d Font.otfCustom font Helvetica Neueascender="716" ascender="975"descender="-212" descender="-217"lineGap="219" lineGap="29"ftxdumperfuser -t hhea -A f Font.otf 62
  63. 63. anyvoid.ru fb.com/anyvoid github.com/anyvoidЗадавайте вопросы:@kosyakveselitkiselev@anyvoid.ru
  64. 64. Кофе-брейк

    Be the first to comment

    Login to see the comments

  • morozovevm

    Mar. 12, 2015

Views

Total views

4,346

On Slideshare

0

From embeds

0

Number of embeds

3

Actions

Downloads

20

Shares

0

Comments

0

Likes

1

×