質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

2206閲覧

Swift UITextView で text 部分以外に gesture を付与する事は可能ですか?

Hayato1201

総合スコア220

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2020/12/27 07:57

編集2020/12/27 11:26

UITextViewに以下の様にtapGestureを付与したいのですが、その際に文字が入力されている部分をタップしても反応しない様にしたいです。その様な挙動は可能でしょうか?
現状これだと文字の上をtapしても反応してしまいます。

Swift

1let uiView = UITextView() 2let tapGestureRecognizer = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.tapGesture)) 3uiView.addGestureRecognizer(tapGestureRecognizer)

追記

入力した文字をタップするとタップした場所にカーソルが動きますが、今回 UITextView 自体にGestureRecognizer を追加しているのでそれによって文字をタップした際にそのジェスチャーが呼ばれてしまいます。そのジェスチャーによって呼ばれるメソッドでは UITextView の表示を消すというものなので、カーソルを移動させようと文字をタップすると TextView が消えてしまします。それを避けたいです。
ですので最終的に実現したい事はUITextViewの文字が入力されている以外の部分をtapしてメソッドが呼ばれ TextView が非表示になるという挙動です。そして文字をタップした場合は普通にカーソルが移動します。

以下の様に無理やり呼んだメソッドの先でテキストの範囲の座標をtapした場合は何もしないという様にする事はできますが、それだとテキストをタップした際にカーソルの移動がうまくいかなくなってしまいます。。。

Swift

1 @objc func tapGesture(gestureRecognizer: UITapGestureRecognizer){ 2 3 if let uiView = gestureRecognizer.view as? UITextView { 4 let range = (uiView.text as NSString).range(of: uiView.text) 5 let rect = uiView.layoutManager.boundingRect(forGlyphRange: range, in: uiView.textContainer) 6 7 let tapPoint = gestureRecognizer.location(in:uiView) 8 9 if !(tapPoint.x > rect.origin.x && tapPoint.x < rect.origin.x + rect.size.width 10 && tapPoint.y > rect.origin.y && tapPoint.y < rect.origin.y + rect.size.height) { 11 doSomething() 12 } 13 14 } 15 }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TsukubaDepot

2020/12/27 08:24

「文字が入力されている部分をタップしても反応しない様にしたい」ということですが、その部分がよく理解できません。 UITextView なので、文字の入力を行わせたいということだと思いますが、一度入力したテキスト部分にはカーソルを表示させたくないということでしょうか。 それとも、ロングタップで表示される編集メニューを出さないようにしたいということでしょうか。 また、あえて GestureRecognizer を追加しているのは、どのような意図からでしょうか。 何らかの Gesture をご自身で追加したいので、GestureRecognizer を追加されたと思いますが、それはどのような目的で使われる予定でしょうか。 最終目的が明確でないため、このままだとおそらく意図した回答はつかないと思いますので、「このような挙動にしたい」だけでなく、もっと広い範囲で想定した操作について、ご質問本文に追記していただければと思います。
Hayato1201

2020/12/27 08:38 編集

コメントありがとうございます。 入力した文字をタップするとタップした場所にカーソルが動きますが、今回 UITextView 自体にGestureRecognizer を追加しているのでそれによって文字をタップした際にそのジェスチャーが呼ばれてしまいます。そのジェスチャーによって呼ばれるメソッドでは UITextView の表示を消すというものなので、カーソルを移動させようと文字をタップすると TextView が消えてしまします。それを避けたいです。 ですので最終的に実現したい事はUITextViewの文字が入力されている以外の部分をtapしてメソッドが呼ばれ TextView が非表示になるという挙動です。そして文字をタップした場合は普通にカーソルが移動します。
Hayato1201

2020/12/27 09:00

念のため質問の方にも同じ内容を追記しました。
MasatoUchida

2020/12/27 13:15

要件定義が曖昧なのですが、同じ「タップ」の操作を同じViewの同じ箇所つけ、その処理を分岐するということでしょうか? 仕様を画像で用意するなど視覚的な情報をあれば、より回答しやすく助かります。
Hayato1201

2020/12/28 02:14

すみません、下記の回答で解決できたのでクローズとさせて頂きます。
guest

回答1

0

ベストアンサー

たとえば、このような感じの実装でしょうか。

tapGestureRecognizer を乗せると、タップした部分にカーソルを移動させる処理も奪われるので、その部分の実装を加えています。

また、明示的に UITextView を First Responder にしなければ編集できなさそうなので、その処理も加えています(逆に、編集不可にする場合には 明示的に Resign しなければならない)。

もしかしたら、こんなことしなくても適切なメソッドを override するなどすればもっとシンプルに解決するかもしれません。

Swift

1 @objc func tapGesture(_ recognizer: UITapGestureRecognizer) { 2 if recognizer.state == .ended { 3 // textView を FirstResponder にする 4 textView.isEditable = true 5 textView.becomeFirstResponder() 6 7 // 範囲内外の判定 8 if let uiView = recognizer.view as? UITextView { 9 let range = (uiView.text as NSString).range(of: uiView.text) 10 let rect = uiView.layoutManager.boundingRect(forGlyphRange: range, in: uiView.textContainer) 11 12 let tapPoint = recognizer.location(in:uiView) 13 14 if !(tapPoint.x > rect.origin.x && tapPoint.x < rect.origin.x + rect.size.width 15 && tapPoint.y > rect.origin.y && tapPoint.y < rect.origin.y + rect.size.height) { 16 17 // 範囲外の場合の処理 18 print("空白部分をタップ") 19 // textView.isHidden = true 20 // textView.resignFirstResponder() 21 } 22 23 } 24 25 // タップした部分にカーソルを移動させる 26 // https://stackoverflow.com/questions/20663614/how-to-move-cursor-of-uitextview-to-touched-location 27 let location = recognizer.location(in: textView) 28 if let position = textView.closestPosition(to: location) { 29 let uiTextRange = textView.textRange(from: position, to: position) 30 31 if let start = uiTextRange?.start, let end = uiTextRange?.end { 32 let loc = textView.offset(from: textView.beginningOfDocument, to: position) 33 let length = textView.offset(from: start, to: end) 34 35 textView.selectedRange = NSMakeRange(loc, length) 36 37 } 38 } 39 } 40 }

投稿2020/12/27 23:11

TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Hayato1201

2020/12/28 02:13

回答ありがとうございました!理想通りの挙動ができる様になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問