You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现带圆角灰色背景标签的UITextView富文本?

要在UITextView里实现带圆角灰色背景的标签,NSAttributedString本身确实没法直接设置圆角背景,给你两个靠谱的原生实现思路:

方法1:自定义NSTextAttachment(简单易上手)

这个思路是把带圆角的标签做成图片,再作为附件插入到富文本中,步骤如下:

  1. 给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
  1. 写工具方法将标签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;
}
  1. 将图片包装成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自定义布局管理器,直接绘制圆角背景:

  1. 子类化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
  1. 给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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:40:53