iOS中如何设置自定义NSTextAttachmentViewProvider视图的尺寸?
解决UITextView中自定义NSTextAttachmentViewProvider无法设置尺寸的问题
在iOS15+中使用NSTextAttachmentViewProvider在UITextView中嵌入自定义UIView时,直接设置NSTextAttachment.bounds会导致自定义视图无法渲染,转而显示默认的缺失图片占位符。默认自定义视图尺寸为37x48,修改尺寸时出现异常。
问题原因
直接设置NSTextAttachment.bounds会让系统判定该附件为图像类型附件,优先尝试用bounds渲染图像占位,而非调用你注册的自定义NSTextAttachmentViewProvider加载视图。这是系统的优先级逻辑:当attachment存在bounds或image时,会优先走图像渲染路径,忽略自定义视图提供者。
解决方案
不要直接修改NSTextAttachment.bounds,而是通过自定义NSTextAttachment子类传递尺寸参数,在NSTextAttachmentViewProvider中根据参数设置视图尺寸;或者直接在Provider内部固定/动态设置视图约束。
方案1:自定义Attachment传递尺寸
- 创建自定义NSTextAttachment子类,添加尺寸属性:
@interface MyTextAttachment : NSTextAttachment @property (nonatomic, assign) CGSize customSize; @end @implementation MyTextAttachment @end
- 修改MyViewProvider,在
loadView中根据attachment的customSize设置视图尺寸:
@implementation MyViewProvider - (void)loadView { self.view = [UIView new]; self.view.backgroundColor = [UIColor blueColor]; // 从自定义Attachment获取尺寸 MyTextAttachment *attachment = (MyTextAttachment *)self.attachment; if (!CGSizeEqualToSize(attachment.customSize, CGSizeZero)) { // 使用AutoLayout约束更适配文本排版 [self.view.widthAnchor constraintEqualToConstant:attachment.customSize.width].active = YES; [self.view.heightAnchor constraintEqualToConstant:attachment.customSize.height].active = YES; } } @end
- 在ViewController中使用自定义Attachment:
- (void)viewDidLoad { [super viewDidLoad]; [NSTextAttachment registerTextAttachmentViewProviderClass:[MyViewProvider class] forFileType:@"public.data"]; MyTextAttachment *firstAttachment = [[MyTextAttachment alloc] initWithData:nil ofType:@"public.data"]; // 不设置尺寸,使用默认 MyTextAttachment *secondAttachment = [[MyTextAttachment alloc] initWithData:nil ofType:@"public.data"]; secondAttachment.customSize = CGSizeMake(20, 20); // 传递自定义尺寸 NSMutableAttributedString *string = [NSMutableAttributedString new]; [string appendAttributedString:[[NSAttributedString alloc] initWithString:@"This is the 1st attachment: "]]; [string appendAttributedString:[NSAttributedString attributedStringWithAttachment:firstAttachment]]; [string appendAttributedString:[[NSAttributedString alloc] initWithString:@"\nThis is the 2nd attachment: "]]; [string appendAttributedString:[NSAttributedString attributedStringWithAttachment:secondAttachment]]; _textView = [UITextView new]; _textView.layer.borderColor = [UIColor redColor].CGColor; _textView.layer.borderWidth = 1.0; _textView.attributedText = string; [self.view addSubview:_textView]; }
方案2:直接在Provider中固定尺寸(适合不需要动态调整的场景)
如果不需要动态传递尺寸,直接在MyViewProvider的loadView里设置视图约束:
@implementation MyViewProvider - (void)loadView { self.view = [UIView new]; self.view.backgroundColor = [UIColor blueColor]; // 直接设置固定尺寸约束 [self.view.widthAnchor constraintEqualToConstant:20].active = YES; [self.view.heightAnchor constraintEqualToConstant:20].active = YES; } @end
关键注意点
- 避免直接修改
NSTextAttachment.bounds,这会触发系统的图像渲染逻辑,绕过自定义视图提供者。 - 使用AutoLayout约束比直接设置frame更可靠,尤其是在UITextView的排版环境中。
- 确保注册的
fileType和Attachment初始化时的ofType一致,否则系统找不到对应的视图提供者。
内容的提问来源于stack exchange,提问作者martega
相关产品推荐
相关产品推荐

