Currently, we are implementing an undo/redo feature in UITextView
.
However, we cannot use the built-in UndoManager
in UITextView
because we have multiple UITextView
instances inside a UICollectionView
.
Since UICollectionView recycles UITextView
instances, the same UITextView
might be reused in different rows, making the built-in UndoManager
unreliable.
The shouldChangeTextIn
method in UITextViewDelegate
is key to implementing undo/redo functionality properly. Here is an example of our implementation:
extension ChecklistCell: UITextViewDelegate {
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
// Get the current text
let s = textView.text ?? ""
// Get the starting position of the change
let start = range.location
// Get the number of characters that will be replaced
let count = range.length
// Get the number of characters that will be added
let after = text.count
print(">>>> The current text = \"\(s)\"")
print(">>>> The starting position of the change = \(start)")
print(">>>> The number of characters that will be replaced = \(count)")
print(">>>> The number of characters that will be added = \(after)")
print(">>>>")
if let delegate = delegate, let checklistId = checklistId, let index = delegate.checklistIdToIndex(checklistId) {
delegate.attachTextAction(s: s, start: start, count: count, after: after, index: index)
}
return true
}
}
Working scene behind the UITextViewDelegate
However, this implementation does not work well with non-English input using an IME. When using an IME, there is an intermediate input before the final input is produced. For example, typing "wo" (intermediate input) produces "我" (final input). Currently, UITextViewDelegate
captures both "wo" and "我".
UITextViewDelegate captures both "wo" and "我"
Is there a way to ignore the intermediate input from IME and only consider the final input?
In Android, we use the beforeTextChanged method in TextWatcher
to seamlessly ignore the intermediate input from IME and only consider the final input. You can see this in action in this
Android captures only "我"
Is there an equivalent way in iOS to ignore the intermediate input from IME and only take the final input into consideration?