Search code examples
swiftuitextviewnsattributedstringundoredo

Implementing undo and redo in a UITextView with attributedText


I'm trying to add undo and redo functionality to my implementation of a UITextView. I am using attributedText and not simply the text property of the UITextView. I've tried using the function calls in undoManager as cited in the Apple documentation, however nothing seems to be happening. I was surprised that I could not find anything on the subject by Googling. Has anyone encountered this issue before / implemented undo and redo on a UITextView with attributedText / knows how to go about this?

Sample Code

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

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

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

Solution

  • Here's some sample code to handle undo/redo for a UITextView. Don't forget to update your undo/redo buttons state initially and after each change to the text.

    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()            
        }
    }
    

    Obviously you'll need to hook up the actions/outlets and the text view's delegate outlet in a storyboard