4

の 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

次に、この配列をループして、この要素を textView の textStorage に連続して適用します。私はこの方法を使用します:

-(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];
    }];
  }

問題が 1 つあります。TextFormattingElement範囲が交差する 2 つのインスタンスがある場合 (例:NSMakeRange(9,28)NSMakeRange(26,7))、下線の太さは常に最後の要素のフォント サイズに依存する値になります。この図は、このスクリーンショットで見ることができます:

ここに画像の説明を入力

私の2つのフォーマット要素のパラメータは次のとおりです。

1 つ目: 位置 = 9、長さ = 28、fontName = TimesNewRomanPSMT、fontSize = 15、fontColor = UIDeviceRGBColorSpace 1 0 0 1、isBold = 0、isItalic = 0、isUnderlined = 1、isStriked = 0

2番目: 位置 = 26、長さ = 7、fontName = TimesNewRomanPSMT、fontSize = 25、fontColor = UIDeviceRGBColorSpace 0 0 1 1、isBold = 1、isItalic = 0、isUnderlined = 1、isStriked = 0

しかし、Google ドキュメントのような効果を得たい:

ここに画像の説明を入力

TextKit を使用してこれを行うにはどうすればよいですか?

4

1 に答える 1

9

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);
}

サンプル

于 2014-02-25T14:20:46.923 に答える