When UITextView contains an attachment ahead and select the rest of the text without the attachment, the delegate method's range parameter is obviously wrong!
Code for bug reproduction:
//
// TestViewController.swift
// Notes
//
// Created by laishere on 2023/12/13.
//
import UIKit
class TestViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
initView()
}
private func initView() {
let textView = UITextView(frame: view.bounds)
textView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(textView)
NSLayoutConstraint.activate([
textView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
textView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor),
textView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor),
])
textView.delegate = self
let attachment = NSTextAttachment(image: UIImage(systemName: "sun.max.fill")!)
textView.textStorage.insert(NSAttributedString(attachment: attachment), at: 0)
let str = NSMutableAttributedString(string: "hello")
str.addAttribute(.foregroundColor, value: UIColor.systemBlue, range: NSRange(location: 0, length: str.length))
textView.textStorage.insert(str, at: 1)
}
}
extension TestViewController : UITextViewDelegate {
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let sel = textView.selectedRange
if sel != range {
print("total text count: \(textView.text!.count), replacing range: \(range), selected range: \(sel)")
}
return true
}
func textViewDidChange(_ textView: UITextView) {
print("text: \(textView.text!), count: \(textView.text!.count)")
}
}
Reproduction procedure:
- Run the TestViewController
- Select
hello
- Press
delete
key - See the delegate params output
As you can see in the above image, the selected range is {1, 5}, but the delegate param range
is {0, 6}.
However, despite the delegate method tells us it want to replace the whole 6 characters with empty string, the actual number of deleted characters is 5.
One charater which is the attachment charater is left as you can see in the output of textViewDidChange
delegate method.