简体   繁体   中英

UITextView, NSAttributedString and custom attributes

I have searched a lot on Stack Overflow but I couldn't find a solution. Perhaps I just misinterpreted some answers.

I have created a UITextView and I am using NSAttributedStrings to work with the UITextView which is just fine.

Now, after adding a custom attribute, I am stuck.

Where can I hook in to render my custom attribute within the UITextView ? Is there a delegate method, or will I have to create my own UITextView and overwrite a method?

You can custom NSLayoutManager , and implement it's -drawGlyphsForGlyphRange:atPoint: method.

For example, you want a custom background with a corner radius

textView init:

NSTextStorage *textStorage = [NSTextStorage new];
CustomLayoutManager *layoutManager = [[CustomLayoutManager alloc] init];
CGSize containerSize = CGSizeMake(self.view.bounds.size.width,  CGFLOAT_MAX);
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:containerSize];

textContainer.widthTracksTextView = YES;
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];

self.textView = [[UITextView alloc] initWithFrame:yourFrame textContainer:textContainer];

And apply your custom attribute:

NSMutableAttributedString *mAttrStr = [[NSMutableAttributedString alloc] initWithString:@"SampleText"];
[mAttrStr addAttribute:YourCustomAttributeName value:[UIColor redColor] range:NSMakeRange(0, mAttrStr.length)];  //for example, you want a custom background with a corner radius
[self.textView.textStorage appendAttributedString:mAttrStr];

In CustomLayoutManager.m

-(void)drawGlyphsForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origin {
    NSRange range = [self characterRangeForGlyphRange:glyphsToShow
                                 actualGlyphRange:NULL];
    //enumerate custom attribute in the range
    [self.textStorage enumerateAttribute:YourCustomAttributeName inRange:range options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id  _Nullable value, NSRange range, BOOL * _Nonnull stop) {
        if (value) {
            UIColor *color = value; //the color set above

            NSRange glyphRange = [self glyphRangeForCharacterRange:range
                                          actualCharacterRange:NULL];
            NSTextContainer *container = [self textContainerForGlyphAtIndex:glyphRange.location
                                        effectiveRange:NULL];

            //draw background
            CGContextRef context = UIGraphicsGetCurrentContext();
            CGContextSaveGState(context);
            CGContextTranslateCTM(context, origin.x, origin.y);
            [color setFill];
            CGRect rect = [self boundingRectForGlyphRange:glyphRange inTextContainer:container];

            //UIBezierPath with rounded
            UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:100];
            [path fill];
            CGContextRestoreGState(context);
            //end draw

            [super drawGlyphsForGlyphRange:range atPoint:origin];
        }
        else {
            [super drawGlyphsForGlyphRange:range atPoint:origin];
        }

    }];
}

Now the 'SampleText' has a red rounded background.

Please refer this simple code snippet to set attributed string to a textview

    let attributedString = NSMutableAttributedString(string:"Test string to add attributes")
    attributedString.addAttributes([NSForegroundColorAttributeName:UIColor.greenColor()], range: NSMakeRange(0, attributedString.string.characters.count))
    textView.attributedText = attributedString

For Objective-C

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]initWithString:@"Test string to add attributes"];
[attributedString addAttributes:@{NSForegroundColorAttributeName:[UIColor greenColor]} range:NSMakeRange(0, attributedString.string.length)];
textView.attributedText = attributedString;

Hope this helps.

If you want apply particular attributes for particular textView rather then string then you should subclass UITextView and make custom initmethod or some method that return UITextView object with specified attribute!! You can pass custom attributes as parameter in method also if attributes are change i mean not fix. and if attribute will remain same implicitly then set attributes in that class by default.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM