如何实现带圆角灰色背景标签的UITextView富文本?
要在UITextView里实现带圆角灰色背景的标签,NSAttributedString本身确实没法直接设置圆角背景,给你两个靠谱的原生实现思路:
方法1:自定义NSTextAttachment(简单易上手)
这个思路是把带圆角的标签做成图片,再作为附件插入到富文本中,步骤如下:
- 给UILabel添加内边距的Category(避免标签文字贴在圆角边缘):
@interface UILabel (Padding) @property (nonatomic, assign) UIEdgeInsets padding; @end @implementation UILabel (Padding) - (void)setPadding:(UIEdgeInsets)padding { objc_setAssociatedObject(self, @selector(padding), [NSValue valueWithUIEdgeInsets:padding], OBJC_ASSOCIATION_RETAIN_NONATOMIC); [self sizeToFit]; } - (UIEdgeInsets)padding { NSValue *value = objc_getAssociatedObject(self, @selector(padding)); return value ? value.UIEdgeInsetsValue : UIEdgeInsetsZero; } - (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines { UIEdgeInsets insets = self.padding; CGRect rect = [super textRectForBounds:UIEdgeInsetsInsetRect(bounds, insets) limitedToNumberOfLines:numberOfLines]; rect.origin.x -= insets.left; rect.origin.y -= insets.top; rect.size.width += insets.left + insets.right; rect.size.height += insets.top + insets.bottom; return rect; } - (void)drawTextInRect:(CGRect)rect { [super drawTextInRect:UIEdgeInsetsInsetRect(rect, self.padding)]; } @end
- 写工具方法将标签View转成UIImage:
+ (UIImage *)tagImageWithText:(NSString *)text fontSize:(CGFloat)fontSize padding:(UIEdgeInsets)padding cornerRadius:(CGFloat)cornerRadius { UILabel *label = [[UILabel alloc] init]; label.text = text; label.font = [UIFont systemFontOfSize:fontSize]; label.backgroundColor = [UIColor lightGrayColor]; label.textColor = [UIColor blackColor]; label.layer.cornerRadius = cornerRadius; label.layer.masksToBounds = YES; label.padding = padding; [label sizeToFit]; UIGraphicsBeginImageContextWithOptions(label.bounds.size, NO, [UIScreen mainScreen].scale); [label.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; }
- 将图片包装成NSTextAttachment插入到富文本:
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:@"普通文本 "]; // 生成标签图片 UIImage *tagImage = [self tagImageWithText:@"标签文本" fontSize:14 padding:UIEdgeInsetsMake(2, 8, 2, 8) cornerRadius:10]; NSTextAttachment *attachment = [[NSTextAttachment alloc] init]; attachment.image = tagImage; // 调整附件与文字的垂直对齐,避免偏移 attachment.bounds = CGRectMake(0, -2, tagImage.size.width, tagImage.size.height); NSAttributedString *tagAttributedText = [NSAttributedString attributedStringWithAttachment:attachment]; [attributedText appendAttributedString:tagAttributedText]; [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@" 更多普通文本"]]; self.textView.attributedText = attributedText;
方法2:自定义NSLayoutManager(更灵活,适合动态内容)
如果标签内容需要动态变化,或者不想转成图片(避免缩放模糊),可以用TextKit自定义布局管理器,直接绘制圆角背景:
- 子类化NSLayoutManager并重写绘制方法:
@interface TagLayoutManager : NSLayoutManager @property (nonatomic, strong) NSArray<NSValue *> *tagRanges; // 存储需要添加背景的文本范围 @end @implementation TagLayoutManager - (void)drawBackgroundForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origin { [super drawBackgroundForGlyphRange:glyphsToShow atPoint:origin]; for (NSValue *rangeValue in self.tagRanges) { NSRange tagRange = rangeValue.rangeValue; // 只处理当前需要绘制的文本范围 if (NSIntersectionRange(tagRange, glyphsToShow).length == 0) continue; // 获取标签文本对应的排版矩形 NSArray *rectArray = [self rectArrayForCharacterRange:tagRange withinSelectedCharacterRange:tagRange inTextContainer:self.textContainers.firstObject]; for (NSValue *rectValue in rectArray) { CGRect rect = rectValue.CGRectValue; rect.origin.x += origin.x; rect.origin.y += origin.y; // 绘制圆角灰色背景 UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:10]; [[UIColor lightGrayColor] setFill]; [path fill]; } } } @end
- 给UITextView配置自定义LayoutManager:
// 构建富文本内容 NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:@"普通文本 标签文本 更多普通文本"]; // 设置标签文本的字体(可选,保证样式统一) NSRange tagRange = [attributedText.string rangeOfString:@"标签文本"]; [attributedText addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:14] range:tagRange]; // 创建TextKit组件 NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedText]; TagLayoutManager *layoutManager = [[TagLayoutManager alloc] init]; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:self.textView.bounds.size]; textContainer.lineFragmentPadding = 0; // 关联组件 [layoutManager addTextContainer:textContainer]; [textStorage addLayoutManager:layoutManager]; // 设置需要添加背景的文本范围 layoutManager.tagRanges = @[[NSValue valueWithRange:tagRange]]; // 绑定到UITextView self.textView.layoutManager = layoutManager; self.textView.textContainer = textContainer; self.textView.attributedText = textStorage;
这个方法直接绘制背景,不会有图片缩放模糊的问题,还支持文本换行时自动调整背景范围。
内容的提问来源于stack exchange,提问作者user1605810
相关产品推荐
相关产品推荐

