SlideShare uma empresa Scribd logo
1 de 64
Core Text
github.com/anyvoid/CoreTextDemo
Константин Киселёв
Ведущий разработчик
Содержание


๏Основы Core Text
• Маркируем области в тексте
• Два столбца
• Обтекание текстом

                               3
Рецепты Юлии Высоцкой




                        4
Рецепты Юлии Высоцкой
          При нажатии на ссылку со
           временем открывается
             установка таймера




                                     5
Рецепты Юлии Высоцкой




                        6
Бар Депозит – Управление Баром
       Во время набора имени в
      поиске участки совпадения
           подсвечиваются в
          выпадающем меню




                                  7
Рендеринг текста

      UIKit

    Core Text
   Quartz Core

                   8
UIKit

• Ничего сложного
• UIFont
• UILabel, UITextView, UIWebView
• Минимальные возможности настройки
  отображения

• в iOS 6 можно задавать NSAttributedString !
                                                9
Рендеринг текста

      UIKit
    Core Text

   Quartz Core

                   10
Quartz Core

• Непосредственная отрисовка глифов
• CGFontRef
• Фиксированная информация о шрифте
• Использование — корректировка
  отображения


                                      11
Quartz Core
CGDataProviderRef 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
Quartz Core
CGDataProviderRef 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
Quartz Core
CGDataProviderRef 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
Рендеринг текста

      UIKit

    Core Text

   Quartz Core

                   15
Core Text

• Оптимальное сложность/возможности
• CTFontRef
• CTFrame, CTLine, CTRun, CTGlyph
• Можно использовать в нескольких
  потоках, но с ограничениями...


                                      16
Core Text
Потокобезопасно            Непотокобезопасно
•   Все функции            •   Объекты, задающие
                               размещение:
•   Объекты, не задающие
    размещение текста:         -   CTFrameSetter

    -   CTFont                 -   CTTypeSetter

    -   CTFontDescriptor       -   CTFrame

    -   ...                    -   CTRun
                               -   CTLine
                               -   ...
                                                   17
Core Text
                         CGPath          CTLine
                                           CTRun
                                     CTGlyph CTGlyph

СFAttributedString   CTFrameSetter        CTRun


                                         CTLine
                     CTTypeSetter       CTFrame


                                                       18
Core Text

• CTFrame – область вывода всего текста
• CTLine – одна строка
• CTRun – последовательность символов с
  одинаковыми атрибутами

• CTGlyph – один символ шрифта
                                          19
Core Text

  This is CTRun    Next	 CTRun   The End.


                  CTLine

                  CTFrame

                                            20
Core Text
• Отраженная и сдвинутая по вертикали
  система координат
- (void)drawRect:(CGRect)rect {
  CGContextRef context = UIGraphicsGetCurrentContext();

   // Делаем текущую матрицу единичной
   CGContextSetTextMatrix( context, CGAffineTransformIdentity );

   // Добавляем к матрице перенос по вертикали
   CGContextTranslateCTM( context, 0, rect.size.height );

   // Добавляем отражение по вертикали
   CGContextScaleCTM( context, 1, -1 );

   // ...

                                                              21
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
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
Core Text
• Полученный CTFrame является моделью, по
  которой отрисовывается текст в
  графическом контексте после вызова
  CTFrameDraw( frame, context );



• Аналогичные функции для отрисовки
  отдельных CTLine, CTRun
  CGContextSetTextPosition(context, p.x, p.y);
  CTLineDraw(line, context);
  CTRunDraw(run, context, CFRangeMake(0, 0));



                                                 24
Core Text
• Полученный CTFrame является моделью, по
  которой отрисовывается текст в
  графическом контексте после вызова
  CTFrameDraw( frame, context );



• Аналогичные функции для отрисовки
  отдельных CTLine, CTRun
  CGContextSetTextPosition(context, p.x, p.y);
  CTLineDraw(line, context);
  CTRunDraw(run, context, CFRangeMake(0, 0));



                                                 25
Core Text
• Полученный CTFrame является моделью, по
  которой отрисовывается текст в
  графическом контексте после вызова
  CTFrameDraw( frame, context );



• Аналогичные функции для отрисовки
  отдельных CTLine, CTRun
  CGContextSetTextPosition(context, p.x, p.y);
  CTLineDraw(line, context);
  CTRunDraw(run, context, CFRangeMake(0, 0));



                                                 26
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
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
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
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
Core Text
y    origin + ascent

    origin       При определенных
origin – descent условиях вещество
                трансформирует межядерный
                фонон

                                       x

                                       31
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
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
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
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
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
Core Text

• Полезные свойства параграфов:
   • межстрочный интервал
   • выравнивание по горизонтали
   • line break mode
   • отступ красной строки
   • ...                           37
Core Text
• CTParagraphStyleSetting
CTParagraphStyleSetting parSet[] = {
    {kCTParagraphStyleSpecifierAlignment, sizeof(value), &value},
    {kCTParagraphStyleSpecifierLineSpacing, ...},
    {kCTParagraphStyleSpecifierLineBreakMode, ...}
    ...
};

• CTParagraphStyleRef
NSInteger optsNum =
  sizeof(parSet) / sizeof(CTParagraphStyleSetting);

CTParagraphStyleRef parStyle =
  CTParagraphStyleCreate( parSet, optsNum);

                                                              38
Core Text

• Установка свойств параграфов доступна:
   - iOS < 6: ТОЛЬКО при инициализации
     NSAttributedString / NSMutableAttributedString

   - iOS 6: можно делать addAttribute у
     NSMutableAttributedString


                                                      39
Core Text
• Неприятности:
   - выделения пишем сами
   - анализ текста (html, rtf и чего угодно
      своего, например, с разбивкой на
      параграфы) тоже пишем сами




                                              40
Core Text
• Приятности:
   ✓ огромные возможности по
     кастомизации вывода текста
   ✓ очень удобно и быстро, когда нужно
     использовать UILabel, но цветной или
     с маркированными участками
   ✓ Достаточно быстр

                                            41
Core Text
• Тест на скорость:
   ‣ UITableView, 5000 ячеек
   ‣ В каждой ячейке label с кастомным
      шрифтом MetaPro-Normal, цвет текста с
      прозрачностью
   ‣ iPhone 4, Xcode Instruments -> CA Instrument



                                                    42
Core Text
• Тест на скорость:
   ‣ UITableView, 5000 ячеек
   ‣ В каждой ячейке label с кастомным
      шрифтом MetaPro-Normal, цвет текста с
      прозрачностью
   ‣ iPhone 4, Xcode Instruments -> CA Instrument

     UILabel                     Core Text
~ 55–56 fps                    ~ 60 fps             43
Core Text
• Тест на скорость:
     ‣ UITableView, 5000 ячеек
     ‣ В каждой ячейке label с кастомным
       шрифтом MetaPro-Normal, цвет текста с
       прозрачностью
     ‣ iPhone 4, Xcode Instruments -> CA Instrument

      UILabelCore Text точно неCore Text
~   55–56 fpsмедленнее ~ 60 fps                       44
Содержание


• Основы Core Text
๏Маркируем области в тексте
• Два столбца
• Обтекание текстом

                              45
Маркировка

• Результаты поиска
• Кастомные ссылки в UILabel   http://anyvoid.ru

➡И все это без UIWebView !

                                              46
Маркировка




             47
Маркировка
• Создаём наследник UIView:
   - selectWithPattern:(NSString *)pattern - метод,
       который будет создавать модель для
       маркировки указанного текста

   -   drawRect:(CGRect)rect - здесь будет
       непосредственная отрисовка текста с
       маркировкой

                                                      48
Маркировка
  • Добавляем какой-нибудь атрибут к
       участкам, которые хотим выделить и
       сохраняем их в в 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
Маркировка
  • Добавляем какой-нибудь атрибут к
       участкам, которые хотим выделить, и
       сохраняем их в в 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
Маркировка

• В drawRect проходим по всем CTRun
  каждой строки и получаем их frame, если
  они входят в сохраненные участки

➡ При проверке подходит ли run
  обращаем внимание на то, что конец
  строки может разорвать CTRun => нужно
  проверять включение, а не совпадение
                                            51
Маркировка
• Маркированным участкам теперь
  соответствуют отдельные CTRun
  CFRange runCFRange = CTRunGetStringRange( run );
  NSValue* parentRangeValue = [self parentForRange: runRange];
  if ( parentRangeValue ) {
     // CTRun подходит, можно получить его frame и использовать это
     ...
  }


• Получаем frame рана с помощью
  CTRunGetTypographicBounds

• Осталось только что-нибудь добавить
  для выделения....                                            52
Маркировка
• Добавим кнопку, 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
Содержание


• Основы Core Text
• Маркируем области в тексте
๏Два столбца
• Обтекание текстом

                               55
Два столбца
• Два столбца делаются просто - выводим
   текст в нескольких CTFrame

    - задаем CGPath для границ столбца
CGRect colRect = CGRectMake(originX, originY, colWidth, colHeight);
CGPathAddRect(rectPath, NULL, colRect);


    - чтобы определить, сколько текста
        вывели в предыдущем столбце,
        используем:
CFRange prevColTextRange =
    CTFrameGetVisibleStringRange(colFrames[columnIdx - 1]);
                                                                 56
Два столбца
• Два столбца делаются просто - выводим
   текст в нескольких CTFrame

    - задаем CGPath для границ столбца
CGRect colRect = CGRectMake(originX, originY, colWidth, colHeight);
CGPathAddRect(rectPath, NULL, colRect);


    - чтобы определить, сколько текста
        вывели в предыдущем столбце,
        используем:
CFRange prevColTextRange =
    CTFrameGetVisibleStringRange(colFrames[columnIdx - 1]);
                                                                 57
Содержание


• Основы Core Text
• Выделяем области в тексте
• Два столбца
๏Обтекание текстом

                              58
Обтекание
• Чтобы текст обтекал shape, нужно
  вырезать последний из основного
  фрейма

    - Не нужно пытаться делать это через
       Core Graphics

    - В Core Text есть специальные
       опции, которые задают вырезаемые
       области
                                           59
Обтекание
  • Каждый вырезаемый                CGPath задается
     через CFDictionaryRef
- (CFDictionaryRef)createClippingPathWithPath:(CGPathRef)pathRef {
    NSDictionary* ret =
        [NSDictionary dictionaryWithObject: (__bridge id)(pathRef)
                      forKey: kCTFramePathClippingPathAttributeName];
    return (__bridge CFDictionaryRef)(ret);
}




                                                                    60
Обтекание
  • Затем массив из CDictionaryRef ставится
      значением опции и передается в
      CTFrameSetterCreateFrame

frameOptionsDict =
   [NSDictionary dictionaryWithObject: clippingPaths
                           forKey: kCTFrameClippingPathsAttributeName];


CTFramesetterCreateFrame(...,
   (__bridge CFDictionaryRef)frameOptionsDict);




                                                                    61
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
anyvoid.ru
                     fb.com/anyvoid
                     github.com/anyvoid

Задавайте вопросы:
@kosyakveselit
kiselev@anyvoid.ru
Кофе-брейк

Mais conteúdo relacionado

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

статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
Andrey Karpov
 
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU
 
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Ontico
 
Система обработки бизнес-логики server-side приложения на Groovy
Система обработки бизнес-логики server-side приложения на GroovyСистема обработки бизнес-логики server-side приложения на Groovy
Система обработки бизнес-логики server-side приложения на Groovy
Regn
 

Semelhante a Core Text: раскрываем интерактивные возможности текста (20)

Rambler.iOS #9: Анализируй это!
Rambler.iOS #9: Анализируй это!Rambler.iOS #9: Анализируй это!
Rambler.iOS #9: Анализируй это!
 
Cpp0x Introduction
Cpp0x IntroductionCpp0x Introduction
Cpp0x Introduction
 
Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...
Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...
Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
 
На что нужно обратить внимание при обзоре кода разрабатываемой библиотеки
На что нужно обратить внимание при обзоре кода разрабатываемой библиотекиНа что нужно обратить внимание при обзоре кода разрабатываемой библиотеки
На что нужно обратить внимание при обзоре кода разрабатываемой библиотеки
 
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru GroupКак не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
 
Canvas API как инструмент для работы с графикой | Odessa Frontend Meetup #18
Canvas API как инструмент для работы с графикой | Odessa Frontend Meetup #18Canvas API как инструмент для работы с графикой | Odessa Frontend Meetup #18
Canvas API как инструмент для работы с графикой | Odessa Frontend Meetup #18
 
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVM
 
Как приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMКак приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVM
 
Использование C++ для низкоуровневой платформозависимой разработки — Кирилл ...
 Использование C++ для низкоуровневой платформозависимой разработки — Кирилл ... Использование C++ для низкоуровневой платформозависимой разработки — Кирилл ...
Использование C++ для низкоуровневой платформозависимой разработки — Кирилл ...
 
AlgoCollections (RUS)
AlgoCollections (RUS)AlgoCollections (RUS)
AlgoCollections (RUS)
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
 
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
Денормализованное хранение данных в PostgreSQL 9.2 (Александр Коротков)
 
Обобщенное программирование в C++ или как сделать свою жизнь проще через стра...
Обобщенное программирование в C++ или как сделать свою жизнь проще через стра...Обобщенное программирование в C++ или как сделать свою жизнь проще через стра...
Обобщенное программирование в C++ или как сделать свою жизнь проще через стра...
 
200 open source проектов спустя: опыт статического анализа исходного кода
200 open source проектов спустя:опыт статического анализа исходного кода200 open source проектов спустя:опыт статического анализа исходного кода
200 open source проектов спустя: опыт статического анализа исходного кода
 
200 open source проектов спустя: опыт статического анализа исходного кода
200 open source проектов спустя: опыт статического анализа исходного кода200 open source проектов спустя: опыт статического анализа исходного кода
200 open source проектов спустя: опыт статического анализа исходного кода
 
Семинар 11. Параллельное программирование на MPI (часть 4)
Семинар 11. Параллельное программирование на MPI (часть 4)Семинар 11. Параллельное программирование на MPI (часть 4)
Семинар 11. Параллельное программирование на MPI (часть 4)
 
Система обработки бизнес-логики server-side приложения на Groovy
Система обработки бизнес-логики server-side приложения на GroovyСистема обработки бизнес-логики server-side приложения на Groovy
Система обработки бизнес-логики server-side приложения на Groovy
 
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
Некоторые паттерны реализации полиморфного поведения в C++ – Дмитрий Леванов,...
 

Último

Cyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdfCyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdf
Хроники кибер-безопасника
 
CVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdfCVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdf
Хроники кибер-безопасника
 
2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf
Хроники кибер-безопасника
 
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
Ирония безопасности
 
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdfСИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
Хроники кибер-безопасника
 
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Ирония безопасности
 

Último (9)

MS Navigating Incident Response [RU].pdf
MS Navigating Incident Response [RU].pdfMS Navigating Incident Response [RU].pdf
MS Navigating Incident Response [RU].pdf
 
Cyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdfCyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdf
 
CVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdfCVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdf
 
2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf
 
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdfMalware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
 
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
 
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdfСИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
 
Ransomware_Q3 2023. The report [RU].pdf
Ransomware_Q3 2023.  The report [RU].pdfRansomware_Q3 2023.  The report [RU].pdf
Ransomware_Q3 2023. The report [RU].pdf
 
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
 

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

  • 3. Содержание ๏Основы Core Text • Маркируем области в тексте • Два столбца • Обтекание текстом 3
  • 5. Рецепты Юлии Высоцкой При нажатии на ссылку со временем открывается установка таймера 5
  • 7. Бар Депозит – Управление Баром Во время набора имени в поиске участки совпадения подсвечиваются в выпадающем меню 7
  • 8. Рендеринг текста UIKit Core Text Quartz Core 8
  • 9. UIKit • Ничего сложного • UIFont • UILabel, UITextView, UIWebView • Минимальные возможности настройки отображения • в iOS 6 можно задавать NSAttributedString ! 9
  • 10. Рендеринг текста UIKit Core Text Quartz Core 10
  • 11. Quartz Core • Непосредственная отрисовка глифов • CGFontRef • Фиксированная информация о шрифте • Использование — корректировка отображения 11
  • 12. Quartz Core CGDataProviderRef 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. Quartz Core CGDataProviderRef 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. Quartz Core CGDataProviderRef 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. Рендеринг текста UIKit Core Text Quartz Core 15
  • 16. Core Text • Оптимальное сложность/возможности • CTFontRef • CTFrame, CTLine, CTRun, CTGlyph • Можно использовать в нескольких потоках, но с ограничениями... 16
  • 17. Core Text Потокобезопасно Непотокобезопасно • Все функции • Объекты, задающие размещение: • Объекты, не задающие размещение текста: - CTFrameSetter - CTFont - CTTypeSetter - CTFontDescriptor - CTFrame - ... - CTRun - CTLine - ... 17
  • 18. Core Text CGPath CTLine CTRun CTGlyph CTGlyph СFAttributedString CTFrameSetter CTRun CTLine CTTypeSetter CTFrame 18
  • 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
  • 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. 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. 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. 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. 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. Core Text • Полезные свойства параграфов: • межстрочный интервал • выравнивание по горизонтали • line break mode • отступ красной строки • ... 37
  • 38. Core Text • CTParagraphStyleSetting CTParagraphStyleSetting parSet[] = { {kCTParagraphStyleSpecifierAlignment, sizeof(value), &value}, {kCTParagraphStyleSpecifierLineSpacing, ...}, {kCTParagraphStyleSpecifierLineBreakMode, ...} ... }; • CTParagraphStyleRef NSInteger optsNum = sizeof(parSet) / sizeof(CTParagraphStyleSetting); CTParagraphStyleRef parStyle = CTParagraphStyleCreate( parSet, optsNum); 38
  • 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
  • 45. Содержание • Основы Core Text ๏Маркируем области в тексте • Два столбца • Обтекание текстом 45
  • 46. Маркировка • Результаты поиска • Кастомные ссылки в UILabel http://anyvoid.ru ➡И все это без UIWebView ! 46
  • 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
  • 55. Содержание • Основы Core Text • Маркируем области в тексте ๏Два столбца • Обтекание текстом 55
  • 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
  • 58. Содержание • Основы Core Text • Выделяем области в тексте • Два столбца ๏Обтекание текстом 58
  • 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
  • 63. anyvoid.ru fb.com/anyvoid github.com/anyvoid Задавайте вопросы: @kosyakveselit kiselev@anyvoid.ru

Notas do Editor

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n