如何移除并重新添加带滚动的NSTextView,解决切换表格单元格时撤销重做异常问题
问题修复与最优解决方案
一、scrollView显示空白的核心原因
- 每次调用
updateView时重复添加约束,旧约束没有被移除,产生约束冲突导致布局失效,视图无法正常渲染 - 单独声明的
textView属性完全冗余,NSTextView.scrollableTextView()生成的滚动视图自带了documentView,多余的实例声明会导致取值逻辑混乱 - 每次移除再添加视图的操作本身没有必要,属于不必要的性能开销
二、显示问题修复代码
第一步:清理冗余属性
删掉你单独声明的textView全局变量,统一从scrollView中取文本视图实例:
var scrollView: NSScrollView = { let scroll = NSTextView.scrollableTextView() scroll.translatesAutoresizingMaskIntoConstraints = false return scroll }() // 存储约束避免重复添加 var scrollViewConstraints: [NSLayoutConstraint]?
第二步:修改约束添加逻辑
每次加约束前先失效旧约束,避免冲突:
func setUpConstraints() { // 先移除旧约束 if let old = scrollViewConstraints { NSLayoutConstraint.deactivate(old) } let newConstraints = [ scrollView.topAnchor.constraint(equalTo: self.topAnchor, constant: 10), scrollView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 15), scrollView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -14), scrollView.bottomAnchor.constraint(lessThanOrEqualTo: self.bottomAnchor, constant: -20) ] scrollViewConstraints = newConstraints NSLayoutConstraint.activate(newConstraints) }
第三步:简化updateView逻辑
func updateView() { self.scrollView.removeFromSuperview() self.addSubview(scrollView) self.setUpConstraints() // 取textView直接用这个方式就行 guard let textView = scrollView.documentView as? NSTextView else { return } // 后续赋值逻辑 }
三、更优的撤销/重做解决方案(无需重建视图)
你完全不需要通过移除再添加视图的方式重置撤销链路,以下方案更高效:
方案1:切换单元格时清空撤销栈(不需要每个文本单独保留撤销记录的场景)
每次切换单元格更新文本前,直接清空当前textView的撤销栈:
func updateText(newAttributedString: NSAttributedString) { guard let textView = scrollView.documentView as? NSTextView else { return } // 清空旧的撤销记录 textView.undoManager?.removeAllActions() // 更新文本 textView.textStorage?.setAttributedString(newAttributedString) }
方案2:每个单元格独立保留撤销记录(推荐)
给你的单元格对应的数据模型新增一个undoManager属性,切换单元格时直接替换textView的undoManager即可,每个文本的撤销操作完全独立不会混乱:
// 你的单元格数据模型 class CellModel { var content: NSAttributedString let undoManager = UndoManager() // 每个模型自带独立撤销栈 // 其他属性... } // 切换单元格更新文本时 func updateSelectedCell(model: CellModel) { guard let textView = scrollView.documentView as? NSTextView else { return } // 替换撤销管理器 textView.undoManager = model.undoManager // 更新文本 textView.textStorage?.setAttributedString(model.content) }
内容的提问来源于stack exchange,提问作者Relstoch
相关产品推荐
相关产品推荐

