2014-02-19 3 views
2

Я работаю с атрибутами textStorage UITextView. У меня есть строка и массив объектов моего класса TextFormattingElement. Экземпляр этого класса состоит из NSRange (на котором этот элемент должен быть применен в тексте) и некоторые параметры форматирования:Подчеркнутый текст - толщина линии в пересекающихся диапазонах с разными размерами шрифтов

@interface TextFormattingElement : NSObject 

@property (nonatomic) NSRange range; 
@property (nonatomic, strong) NSString *fontName; //e.g. @"TimesNewRomanPSMT" 
@property (nonatomic) int fontSize; 
@property (nonatomic, strong) UIColor *fontColor; 
@property (nonatomic) BOOL isBold; 
@property (nonatomic) BOOL isItalic; 
@property (nonatomic) BOOL isUnderlined; 
@property (nonatomic) BOOL isStriked; 

@end 

Теперь я цикл через этот массив и последовательно применять эти элементы к textStorage из TextView. Я использую этот метод:

-(void)setFontWithName:(NSString*)name AndSize:(float)fontSize AndTextColor:(UIColor*)textColor AndIsBold:(BOOL)isBold AndIsItalic:(BOOL)isItalic AndIsUnderlined:(BOOL)isUnderLined andIsStriked:(BOOL)isStriked ToRange:(NSRange)rangeToSet{ 
    __block UIFont *font = [UIFont fontWithName:name size:fontSize]; 
    __block UIFontDescriptor *fontDescriptor = [font fontDescriptor]; 

    [textView.textStorage enumerateAttributesInRange:rangeToSet options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { 
     NSParagraphStyle *paragraphStyle = [attrs objectForKey:NSParagraphStyleAttributeName]; 

     NSMutableDictionary *attributesToSetDict = [NSMutableDictionary dictionary]; 
    [attributesToSetDict setObject:paragraphStyle forKey:NSParagraphStyleAttributeName]; //i need to clear all attributes at this range exсept NSParagraphStyleAttributeName 

     if(isBold){ 
      uint32_t existingTraitsWithNewTrait = [fontDescriptor symbolicTraits] | UIFontDescriptorTraitBold; 
      fontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:existingTraitsWithNewTrait]; 
     } 

     if(isItalic){ 
      uint32_t existingTraitsWithNewTrait = [fontDescriptor symbolicTraits] | UIFontDescriptorTraitItalic; 
      fontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:existingTraitsWithNewTrait]; 
     } 

     font = [UIFont fontWithDescriptor:fontDescriptor size:fontSize]; 
     [attributesToSetDict setObject:font forKey:NSFontAttributeName]; 
     [attributesToSetDict setObject:textColor forKey:NSForegroundColorAttributeName]; 

     if(isUnderLined){ 
      [attributesToSetDict setObject:@1 forKey:NSUnderlineStyleAttributeName]; 
     } 

     if(isStriked){ 
      //TODO: isStriked 
     } 

     [textView.textStorage setAttributes:attributesToSetDict range:range]; 
    }]; 
    } 

меня есть одна проблема: если у меня есть два TextFormattingElement экземпляры с диапазонами пересечения (например NSMakeRange(9,28) и NSMakeRange(26,7)) толщина подчеркивание всегда имеет значение, зависящее от размера шрифта последнего элемента. Иллюстрацией этого вы можете увидеть на этом скриншоте:

enter image description here

Параметры моих 2-х элементов форматирования являются:

первый: место = 9, длина = 28, Fontname = TimesNewRomanPSMT, FONTSIZE = 15, FontColor = UIDeviceRGBColorSpace 1 0 0 1, isBold = 0, isItalic = 0, isUnderlined = 1, isStriked = 0

второе: место = 26, длина = 7, FontName = TimesNewRomanPSMT, FontSize = 25, FontColor = UIDeviceRGBColo rSpace 0 0 1 1, isBold = 1, isItalic = 0, isUnderlined = 1, isStriked = 0

Но я хочу, чтобы получить такой эффект, как в Google Docs:

enter image description here

Как я могу делать это с помощью TextKit?

ответ

7

На iOS 7 вы можете делать то, что хотите, с подклассом NSLayoutManager, который переопределяет -drawUnderlineForGlyphRange:underlineType:baselineOffset:lineFragmentRect:lineFragmentGlyphRange:containerOrigin: Небольшой образец, который создает объекты текстовой системы и нарисовывает нужную подчеркивание.

@implementation ViewController 
- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    // setup text handling with our subclass of NSLayoutManager 
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithString:@"Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do;"]; 

    MyLayoutManager *textLayout = [[MyLayoutManager alloc] init]; 

    [textStorage addLayoutManager:textLayout]; 

    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:self.view.bounds.size]; 

    [textLayout addTextContainer:textContainer]; 
    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0,20,self.view.bounds.size.width,self.view.bounds.size.height-20) 
               textContainer:textContainer]; 
    [self.view addSubview:textView]; 

    // setup test attributes 
    UIFont *font = [UIFont fontWithName:@"TimesNewRomanPSMT" size:15]; 

    NSMutableDictionary *attributesToSetDict = [NSMutableDictionary dictionary]; 

    [attributesToSetDict setObject:font forKey:NSFontAttributeName]; 
    [attributesToSetDict setObject:[UIColor blueColor] forKey:NSForegroundColorAttributeName]; 
    [attributesToSetDict setObject:@1 forKey:NSUnderlineStyleAttributeName]; 

    [textView.textStorage setAttributes:attributesToSetDict range:NSMakeRange(9, 28)]; 

    UIFontDescriptor *fontDescriptor = [font fontDescriptor]; 
    attributesToSetDict = [NSMutableDictionary dictionary]; 
    [attributesToSetDict setObject:[UIFont fontWithDescriptor:fontDescriptor size:25] forKey:NSFontAttributeName]; 
    [attributesToSetDict setObject:[UIColor redColor] forKey:NSForegroundColorAttributeName]; 
    [attributesToSetDict setObject:@1 forKey:NSUnderlineStyleAttributeName]; 

    [textView.textStorage setAttributes:attributesToSetDict range:NSMakeRange(26, 7)]; 
} 
@end 

@implementation MyLayoutManager 
- (void)drawUnderlineForGlyphRange:(NSRange)glyphRange underlineType:(NSUnderlineStyle)underlineVal baselineOffset:(CGFloat)baselineOffset lineFragmentRect:(CGRect)lineRect lineFragmentGlyphRange:(NSRange)lineGlyphRange containerOrigin:(CGPoint)containerOrigin 
{ 
    /* For the sample we consider that underlineVal is equals to NSUnderlineStyleSingle */ 

    /* 
    * We need to create a CTFontRef as UIFont doesn't provided 'Underline Position' and 'Underline Thickness' 
    */ 

    // get current UIFont for the glyphRange 
    NSRange characterRange = [self characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL]; 
    UIFont *font = [[self.textStorage attributesAtIndex:characterRange.location effectiveRange:NULL] objectForKey:NSFontAttributeName]; 

    CTFontRef ctfont = CTFontCreateWithName((CFStringRef)[font fontName], [font pointSize], NULL); 

    CGPoint startLocation = [self locationForGlyphAtIndex:glyphRange.location]; 
    CGPoint endLocation = [self locationForGlyphAtIndex:NSMaxRange(glyphRange)]; 

    CGContextRef ctx = UIGraphicsGetCurrentContext(); 

    // -underlineGlyphRange:underlineType:lineFragmentRect:lineFragmentGlyphRange:containerOrigin: already set color and line width 
    // for the current context 

    // reset line width with current font underline thickness 
    CGContextSetLineWidth(ctx, CTFontGetUnderlineThickness(ctfont)); 

    CGContextMoveToPoint(ctx, startLocation.x + containerOrigin.x, startLocation.y + containerOrigin.y - CTFontGetUnderlinePosition(ctfont)); 
    CGContextAddLineToPoint(ctx, endLocation.x + containerOrigin.x, endLocation.y + containerOrigin.y - CTFontGetUnderlinePosition(ctfont)); 

    CGContextStrokePath(ctx); 

    CFRelease(ctfont); 
} 

sample

+0

Спасибо! Я также начал работать в этом направлении, но имел проблему с поиском формулы для подчеркивания толщины и подчеркивания отступа от персонажей. Теперь с вашим ответом я знаю, как это сделать с помощью Core Text. Большое спасибо вам тысячи раз !!! – xexe

+0

@xexe Добро пожаловать. – Emmanuel