on iOS, when VoiceOver is running, double click the UITextView can quickly switch the cursor between the beginning and end of the text.
Problem
the problem is, when the text contains some NSTextAttachment, this ability has failed.
Here is some code to reproduce this problem.
import UIKit
class ViewController: UIViewController {
private var textView: UITextView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.backgroundColor = UIColor.white
textView = UITextView(frame: CGRect(x: 60, y: 220, width: 240, height: 180))
textView?.layer.borderColor = UIColor.black.cgColor
textView?.layer.borderWidth = 1
textView?.font = .systemFont(ofSize: 18)
view.addSubview(textView!)
let button = UIButton(type: .system)
button.frame = CGRect(x: 120, y: 120, width: 60, height: 60)
button.setTitle("click", for: .normal)
button.addTarget(self, action: #selector(self.click), for: .touchUpInside)
view.addSubview(button)
}
@objc func click() {
let emojiImage = UIImage(systemName: "iphone.circle")
let lineHeight = UIFont.systemFont(ofSize: textView!.font?.pointSize ?? 0.0).lineHeight
let insertedAttr = generateAttributedString(image: emojiImage, bounds: CGRect(x: 0, y: -4, width: lineHeight, height: lineHeight))
let attr = NSMutableAttributedString(attributedString: textView!.attributedText)
attr.replaceCharacters(in: textView!.selectedRange, with: insertedAttr)
textView!.attributedText = attr
}
public func generateAttributedString(image: UIImage?, bounds: CGRect) -> NSMutableAttributedString {
let attachment = NSTextAttachment(data: nil, ofType: nil)
attachment.bounds = bounds
attachment.image = image
let attrString = NSMutableAttributedString(attributedString: NSAttributedString(attachment: attachment))
attrString.addAttribute(.font, value: textView!.font!, range: NSRange(location: 0, length: attrString.length))
return attrString
}
}