2021年9月26日日曜日

リンクタップ可能でかつ選択不可なUITextViewを作る

次なような、UITextViewを作成することにしました。

1. リンクタップは可能である、その他のテキストは操作無反応とする
2. ダブルタップによるテキスト選択動作は不可である
3. ロングタップは不可である


UITextViewのカスタムViewを作成する

条件1から3まで満たすようなカスタムViewを作ります。
この方法は、Stackoverflowに書かれているものを参考にしました。
UITextView: Disable selection, allow links


  1. class UnselectableTextView: UITextView {
  2. override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
  3. guard let position = closestPosition(to: point) else { return false }
  4. guard let range = tokenizer.rangeEnclosingPosition(
  5. position,
  6. with: .character,
  7. inDirection: UITextDirection(rawValue: UITextLayoutDirection.left.rawValue)
  8. ) else { return false }
  9. let startIndex = offset(from: beginningOfDocument, to: range.start)
  10. return attributedText.attribute(.link, at: startIndex, effectiveRange: nil) != nil
  11. }
  12.  
  13. override func becomeFirstResponder() -> Bool {
  14. return false
  15. }
  16. }


override func point() にて、タップした位置のテキストがリンクであるかどうか判定し、override func becomeFirstResponder() にてダブルタップとロングタップを抑制しています。
ViewControllerに実装するときは、次のようになります。


  1. class ViewController: UIViewController {
  2.  
  3. @IBOutlet weak var textView: UnselectableTextView!
  4. override func viewDidLoad() {
  5. super.viewDidLoad()
  6. setupViews()
  7. }
  8.  
  9. private func setupViews() {
  10. textView.delegate = self
  11. textView.isEditable = false
  12. textView.isSelectable = true
  13. textView.backgroundColor = .clear
  14.  
  15. let attributedString = NSMutableAttributedString(string: "this textview has a link test.")
  16. attributedString.addAttribute(.link, value: "https://www.google.com", range: NSRange(location: 20, length: 4))
  17. textView.attributedText = attributedString
  18. }
  19. }
  20.  
  21. extension UIViewController: UITextViewDelegate {
  22. public func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
  23. print(URL.absoluteString)
  24. return false
  25. }
  26. }


ロングタップをハンドリングする

上記で作成したUnselectableTextViewに、独自の UILongPressGestureRecognizer を組み込もうとしたが断念。
UITextViewの GestureRecognizer とConflictしてしまい、良い感じで実装できずでした。
代替案として、UnselectableTextViewの後ろにUIViewを配置して、このUIViewにてロングタップをハンドリングさせます。


  1. class ViewController: UIViewController {
  2. private func setupGesture() {
  3. let longPressGesture = UILongPressGestureRecognizer(
  4. target: self,
  5. action: #selector(ViewController.longPress(_:))
  6. )
  7. longPressGesture.delegate = self
  8. backgroundView.addGestureRecognizer(longPressGesture)
  9. }
  10. }
  11.  
  12. extension UIViewController: UIGestureRecognizerDelegate {
  13. @objc func longPress(_ sender: UILongPressGestureRecognizer) {
  14. print(sender.state.rawValue.description)
  15. }
  16. }

0 件のコメント:

コメントを投稿