在UITextView中使用attributedText实现撤销和重做

4
我正在尝试为我的UITextView实现撤销重做功能。我正在使用attributedText而不是UITextViewtext属性。我已经尝试使用undoManager中引用的函数调用,如Apple文档所述,但似乎没有任何反应。我很惊讶我在Google上找不到有关这个主题的任何信息。有没有人遇到过这个问题或者在UITextView上实现attributedText的撤销和重做功能,或者知道如何解决这个问题? 示例代码:
textView.attributedText = NSMutableAttributedString(string: "SOME TEXT")

@objc func undo(_ sender: UIButton) {
    textView.undoManager?.undo()
}

@objc func redo(_ sender: UIButton) {
    textView.undoManager?.redo()
}

1
你能否包含代码以显示你正在注册撤销操作的位置? - sanch
@sanch 是的,这就是问题所在,但不确定如何注册所有属性等。@AbecedarioPoint 我采用了您的编辑建议,但实际上这些函数是以编程方式调用的,因此不需要使用@IBAction - Anters Bear
1
我认为这篇文章会回答你大部分的问题。https://dev59.com/RWAf5IYBdhLWcg3wu0xD#32596899 - sanch
1
@AbecedarioPoint,你的修改是不正确的。编程UI不需要IBAction,它的字面意思是InterfaceBuilderAction。OP设置了objc句柄是正确的,因为选择器是一个objc方法,并且在Swift 4中不再隐含类型推断。 - sanch
@sanch,感谢你的所有帮助和建议。我刚刚使用[Int:String]来跟踪内容,使用[Int:NSRange]来跟踪所选范围,并使用一个简单的计数器来跟踪撤销/重做索引,从头开始实现了自己的撤销/重做版本。效果还不错! - Anters Bear
显示剩余3条评论
2个回答

6

这里是处理UITextView撤销/重做的示例代码。不要忘记在文本更改后和最初更新您的撤销/重做按钮状态。

class ViewController: UIViewController {

    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var undoButton: UIButton!
    @IBOutlet weak var redoButton: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        updateUndoButtons()
    }

    @IBAction func undo(_ sender: Any) {
        textView.undoManager?.undo()
        updateUndoButtons()
    }

    @IBAction func redo(_ sender: Any) {
        textView.undoManager?.redo()
        updateUndoButtons()
    }

    func updateUndoButtons() {
        undoButton.isEnabled = textView.undoManager?.canUndo ?? false
        redoButton.isEnabled = textView.undoManager?.canRedo ?? false
    }
}        

extension ViewController: UITextViewDelegate {

    func textViewDidChange(_ textView: UITextView) {
        updateUndoButtons()            
    }
}

显然,您需要在Storyboard中连接操作/输出和文本视图的代理输出。

谢谢!这绝对是最好的解决方案。我已经从头开始实现了自己的版本,包含我想要的功能/怪癖,但我希望这对于任何想在UITextView上实现撤销/重做的人都有用。 - Anters Bear

1

这不是OP问题的解决方案,而是一个简单的替代方案

我以前没有处理过这个问题,但我认为可以通过实现堆栈数据结构来与UITextField委托回调textViewDidFinishEditing(textField: UITextField)相结合来解决。想法是,每当用户更改文本字段时,将当前的属性字符串放入堆栈中。通过将按钮与您的堆栈连接起来,并弹出最近的属性字符串,然后设置textfield的属性字符串属性来使用撤销功能。


1
不需要自己编写 - 这是免费的。每个UIResponder都有一个可选的undoManager,它处理推入/弹出堆栈。 - Ashley Mills
@AshleyMills,您说得很正确。我已经在我的回答中添加了一个限定语句。 - sanch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接