Swift 在带有AttributeText的UITextView中实现撤消和重做

Swift 在带有AttributeText的UITextView中实现撤消和重做,swift,uitextview,nsattributedstring,undo,redo,Swift,Uitextview,Nsattributedstring,Undo,Redo,我正在尝试向我的UITextView实现中添加undo和redo功能。我使用的是attributedText,而不仅仅是UITextView的text属性。我已经尝试使用中引用的undoManager中的函数调用,但是似乎什么都没有发生。我很惊讶,我在谷歌上找不到关于这个主题的任何东西。在使用attributedText/对UITextView执行撤消和重做之前,是否有人遇到过此问题?是否知道如何执行此操作 示例代码 textView.attributedText = NSMutableAttr

我正在尝试向我的
UITextView
实现中添加undoredo功能。我使用的是
attributedText
,而不仅仅是
UITextView
text
属性。我已经尝试使用中引用的
undoManager
中的函数调用,但是似乎什么都没有发生。我很惊讶,我在谷歌上找不到关于这个主题的任何东西。在使用
attributedText
/对
UITextView
执行撤消和重做之前,是否有人遇到过此问题?是否知道如何执行此操作

示例代码

textView.attributedText = NSMutableAttributedString(string: "SOME TEXT")

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

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

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

textView.attributedText = NSMutableAttributedString(string: "SOME TEXT")

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

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

我以前没有处理过这个问题,我认为可以通过实现堆栈数据结构并结合UITextField委托回调
textViewDidFinishEditing(textField:UITextField)
来实现。其思想是,对于用户对文本字段所做的每一次更改,您都将当前属性化字符串放在堆栈上。撤消功能通过将一个按钮连接到堆栈,弹出最近的属性字符串,并相应地设置textfields属性字符串属性来发挥作用

下面是一些用于处理
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()            
    }
}

显然,您需要在故事板中连接操作/出口和文本视图的代理出口

是否可以包含代码以显示注册撤消操作的位置?请确保
iAction
已连接到您的
ui按钮
。我已经测试了您的代码是否对我有效。@sanch yes这就是问题所在,但不确定如何注册所有属性等@abecardiopoint我接受了您的编辑,但实际上函数是通过编程方式调用的,因此不需要
@IBAction
,我认为这将回答您的大多数问题@您的编辑不正确。编程UI不需要iAction,它字面上代表InterfaceBuilderAction。Op设置objc句柄是正确的,因为选择器是一个objc方法,swift 4中不再暗示类型推断,因此无需自己编写,这是免费的。每个
UIResponder
都有一个可选的
undoManager
,用于处理堆栈上的推送/弹出操作。@AshleyMills,你肯定是对的。我在回答中添加了一个限定语句。谢谢!这绝对是最好的解决方案。我已经用我想要的特性/怪癖从零开始实现了我自己的版本,但我希望这对任何想在
UITextView上实现undo/redo的人都有用