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

如何移除并重新添加带滚动的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.22 00:36:07