Reputation: 2143
I am attempting to make something that would make it possible to add custom UIView
subclasses as attachments in UITextView
. As of iOS 15, there appears to be an API that allows you to register custom "view providers" for text attachments, and I've successfully managed to get a custom view showing up in a UITextView
using that API, however I've run into a fairly large road block. I can't figure out how to set the size of one of these text attachment views. Each of my text attachment views gets a fixed size of {.width = 37, .height = 48}
and anything I've done to try to change that results in UITextView
's default rendering for when you have a plain text attachment with no image (just a "missing image" placeholder icon).
In the sample code below, I just setup a view controller that has a single text view which tries to render some text with two custom view attachments. The first attachment just gets the default size that Apple gives it. For the other one I try to set the size, and you can see how it breaks things.
Am I missing something with how to use this API? I feel like I must be otherwise this API is fairly useless? What good is embedding UIView
's in a text view if you can make them the right size?
//--------------------------------------------------------------------
@interface MyViewProvider : NSTextAttachmentViewProvider
@end
@implementation MyViewProvider
- (void)loadView {
self.view = [UIView new];
self.view.backgroundColor = [UIColor blueColor];
}
@end
//--------------------------------------------------------------------
@interface MyViewController ()
@end
@implementation MyViewController {
UITextView *_textView;
}
#pragma mark - Initialization
- (void)viewDidLoad {
[super viewDidLoad];
[NSTextAttachment registerTextAttachmentViewProviderClass:[MyViewProvider class] forFileType:@"public.data"];
NSTextAttachment *firstAttachment = [[NSTextAttachment alloc] initWithData:nil ofType:@"public.data"];
NSTextAttachment *secondAttachment = [[NSTextAttachment alloc] initWithData:nil ofType:@"public.data"];
secondAttachment.bounds = CGRectMake(0, 0, 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];
}
#pragma mark - Layout
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
_textView.frame = CGRectInset(self.view.bounds, 20, 60);
}
@end
Upvotes: 2
Views: 384
Reputation: 236
I had the same trouble as you and found a solution by overriding attachmentBounds(for:location:textContainer:proposedLineFragment:position:)
in my NSTextAttachmentViewProvider
.
Here is my code...
NSTextAttachment.registerViewProviderClass(MyAttachmentViewProvider.self, forFileType: "public.item")
final class MyAttachmentViewProvider: NSTextAttachmentViewProvider {
override init(textAttachment: NSTextAttachment,
parentView: ViewType?,
textLayoutManager: NSTextLayoutManager?,
location: NSTextLocation) {
super.init(textAttachment: textAttachment,
parentView: parentView,
textLayoutManager: textLayoutManager,
location: location)
tracksTextAttachmentViewBounds = true
}
override func loadView() {
super.loadView()
view = TestView(frame: .zero)
}
override func attachmentBounds(for attributes: [NSAttributedString.Key: Any],
location: NSTextLocation,
textContainer: NSTextContainer?,
proposedLineFragment: CGRect,
position: CGPoint) -> CGRect {
return CGRect(x: 0, y: 0, width: proposedLineFragment.height, height: proposedLineFragment.height)
}
}
Upvotes: 1