UITextView
has undoManager
that will manage undo and redo for free without requiring any additional code.
Replacing its attributedText
will reset the undoManager
(Updating text and its attributes in textStorage
not work for me either). However, I discovered that undo and redo functionality works normally when formatting text without replacing attributedText
but by standard edit actions (Right click on highlighting text > Font > Bold (Mac Catalyst)).
To ensure that undoManager
works properly, you need to use only certain specific methods of UITextView
. Using other methods may break the undo functionality of UITextView
.
Editing Text
- You need to set the
allowsEditingTextAttributes
of UITextView
to be true
, this will make UITextView
support undo and redo of attributedText
.
self.textView.allowsEditingTextAttributes = true
- If you want to change the text of
attributedText
, use replace(_:withText:)
of UITextInput
, or insertText(_:)
and deleteBackward()
of UIKeyInput
that UITextView
conforming to.
self.textView.replace(self.textView.selectedTextRange!, withText: "test")
Updating Attributes
If you want to change attributes of text, use updateTextAttributes(conversionHandler:)
of UITextView
instead.
self.textView.updateTextAttributes { _ in
let font = UIFont.boldSystemFont(ofSize: 17)
let attributes: [NSAttributedString.Key: Any] = [
.font: font,
]
return attributes
}
or
self.textView.updateTextAttributes { attributes in
let newAttributes = attributes
let font = UIFont.boldSystemFont(ofSize: 17)
let newAttributes: [.font] = font
return newAttributes
}
Inserting Attachments
According to the documentation for init(attachment:)
in NSAttributedString
.
This is a convenience method for creating an attributed string containing an attachment using character
(NSTextAttachment.character) as the base character.
If you want to add an attachment using updateTextAttributes
, you should insert a special character (The attachment will not show up if you are not using this character.) for the attachment first (NSTextAttachment.character
).
For example,
let attachment = NSTextAttachment(image: image)
let specialChar = String(Character(UnicodeScalar(NSTextAttachment.character)!))
textView.insertText(specialChar)
textView.selectedRange = NSRange(location: textView.selectedRange.lowerBound-specialChar.count, length: specialChar.count)
textView.updateTextAttributes { attributes in
var newAttributes = attributes
newAttributes[.attachment] = attachment
return newAttributes
}
For changing text and its attributes in specific range, modify the selectedRange
or selectedTextRange
of UITextView
.
To implement undo and redo buttons, check this answer : https://stackoverflow.com/a/50530040/8637708
I have tested with Mac Catalyst, it should work on iOS and iPadOS too.