从Storyboard创建UITextView实例时,如何注册CustomLayoutManager
解决Storyboard初始化CustomTextView时注册CustomLayoutManager的问题
当你从Storyboard/XIB创建CustomTextView实例时,系统会调用init?(coder:)这个required初始化方法,而非你自定义的init(frame:textContainer:)。所以我们需要在这个方法里也完成CustomLayoutManager的配置,替换掉系统默认的布局管理器。
下面是完整的实现代码:
final class CustomTextView: UITextView { // 代码初始化时的逻辑保持不变 override init(frame: CGRect, textContainer: NSTextContainer?) { let storage = NSTextStorage() let layout = CustomLayoutManager() storage.addLayoutManager(layout) let container = NSTextContainer() container.widthTracksTextView = true layout.addTextContainer(container) super.init(frame: frame, textContainer: container) } // Storyboard/XIB初始化时的核心逻辑 required init?(coder: NSCoder) { super.init(coder: coder) // 确保能获取到当前textContainer,避免空值崩溃 guard let textContainer = self.textContainer else { return } // 解除系统默认组件的关联,避免重复引用或布局冲突 let defaultLayout = textContainer.layoutManager defaultLayout?.removeTextContainer(textContainer) defaultLayout?.textStorage?.removeLayoutManager(defaultLayout!) // 创建自定义的文本处理组件 let customStorage = NSTextStorage() let customLayout = CustomLayoutManager() // 建立正确的关联链:textStorage → layoutManager → textContainer customStorage.addLayoutManager(customLayout) customLayout.addTextContainer(textContainer) // 同步Storyboard中设置的初始文本/属性文本 if let attributedText = self.attributedText { customStorage.setAttributedString(attributedText) } // 替换textView的textStorage,让自定义布局生效 self.textStorage = customStorage } }
关键细节说明:
- 调用
super.init(coder:)后,textView已经自带了默认的textStorage、layoutManager和textContainer,必须先解除它们的关联,否则会出现重复布局或内存泄漏问题。 - 同步原有文本内容很关键,不然你在Storyboard里设置的占位文本、初始文本会丢失。
- 替换
textStorage后,textView会自动使用我们的CustomLayoutManager处理文本布局。
这样不管是通过代码初始化,还是从Storyboard拖入创建CustomTextView,都能正确用上你的CustomLayoutManager啦!
内容的提问来源于stack exchange,提问作者toyu
相关产品推荐
相关产品推荐

