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

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

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

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

Q&A

解決済

1回答

4778閲覧

重なったビューのイベントを両方取得したい

msa_winnie

総合スコア17

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

0グッド

0クリップ

投稿2019/06/11 09:51

問題

ビューに指の動きを表示する機能(シミュレータでいう●印)を実装しております。

UIViewの派生クラスを用意し、ツールバーやナビゲーションバーの上に被せるように追加しています。
self.navigationController?.view.addSubview(PresenView)

参考:https://qiita.com/akatsuki174/items/0681bd314b04fd64eb18

そこで、UIViewControllerの派生クラスとUIViewの派生クラスの両方のイベント(タッチやボタンなど)を拾いたいのですが、良い方法はありますでしょうか。
現在は、UIViewの派生クラスのtouchイベントしか通知されない状況です。

良いやり方をご存じの方がいらっしゃいましたら、ご教示のほどお願い申し上げます。

調べたこと

①hitTest()をオーバーライドすると、指定したどちらか一方のビューへ通知することができるようです。しかし、両方に通知することは難しそうでした。

②UIViewControllerの派生クラスの方のtouchesBegan()などで、PresenView.touchesBegan(touches, with: event)のようにUIViewの派生クラスのタッチイベントを呼び出すことはできそうでした。しかし、今回ナビゲーションバーやタブバーを利用しており、その場合はtouchesBegan()などに来てくれないため、悩んでいます。

該当のソースコード

swift

1class ViewController: UIViewController{ 2 override func viewDidLoad() { 3 super.viewDidLoad() 4   5  // ビューの追加 6 PresenView = PrezenforView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height)) 7 PresenView.backgroundColor = UIColor.clear 8 PresenView.isHidden = false 9 PresenView.isUserInteractionEnabled = true 10 PresenView.isMultipleTouchEnabled = true // マルチタッチ可 11 self.navigationController?.view.addSubview(PresenView) 12 } 13 14 // ----- タッチ関連 ----- 15 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 16 17 // 来ない 18 } 19 override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { 20 21 // 来ない 22 } 23 override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { 24 25 // 来ない 26 } 27 28 // 略 29} 30 31// カスタムビュー 32class PrezenforView: UIView { 33 34 var ptarray:Array<CGPoint> = [] 35 36 override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 37 let view = super.hitTest(point, with: event) 38  39 //if(view == self){ 40     // ここを入れるとPrezenforViewのタッチイベントが発生しない 41 // return nil 42 //} 43 return view 44 } 45 46 // ----- タッチ関連 ----- 47 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 48 49 var arPt:Array<CGPoint> = [] 50 51 for touch in touches { 52 let location = touch.location(in: self) 53 arPt.append(location) 54 } 55 56 Setptarray(array: arPt) 57 } 58 override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { 59 60 var arPt:Array<CGPoint> = [] 61 62 for touch in touches { 63 let location = touch.location(in: self) 64 arPt.append(location) 65 } 66 67 Setptarray(array: arPt) 68 } 69 70 override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { 71 72 Removeptarray() 73 } 74 75 override func draw(_ rect: CGRect) { 76 77 for i in 0..<ptarray.count { 78 // 円の描画 79 let circle = UIBezierPath(arcCenter: CGPoint(x: ptarray[i].x, y: ptarray[i].y), radius:18, startAngle: CGFloat(Double.pi*2.0*0.0/360.0), endAngle:CGFloat(Double.pi*2.0*360.0/360.0), clockwise: true) 80 let circleColor = UIColor(red:0, green:0, blue:1, alpha:0.6) 81 circleColor.setStroke() // 線の色を設定 82 circleColor.setFill() // 内側塗りつぶし色の設定 83 circle.fill() // 内側塗りつぶし 84 circle.lineWidth = 0 85 circle.stroke() 86 } 87 } 88 89 // 座標配列の設定 90 func Setptarray(array: Array<CGPoint>){ 91 ptarray = array 92 93 self.setNeedsDisplay() // 描画更新 94 } 95 96 // 座標配列の削除 97 func Removeptarray(){ 98 ptarray.removeAll() 99 100 self.setNeedsDisplay() // 描画更新 101 } 102 103}

補足情報

Xcode:Version 10.2
Swift5
iOS12.2

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

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

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

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

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

guest

回答1

0

ベストアンサー

こんにちは。

ビューに指の動きを表示する機能(シミュレータでいう●印)を実装しております。

↑ここしか読んでいませんが、
UIWindowの派生クラスを作り、sendEventメソッドをオーバーライドすると、すべてのタッチイベントが取得できますので、そこから煮るなり焼くなりするという方法があります。

#また、そもそもアプリ宣伝動画撮影用に、そうしたOSSが有った気がするのですが、ちょっと検索しても出てきませんでした・・・


追記:

ありました!
https://github.com/morizotter/TouchVisualizer
実装を見てみると参考になると思います。

投稿2019/06/12 00:31

編集2019/06/12 00:36
takabosoft

総合スコア8356

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

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

msa_winnie

2019/06/12 02:49

takabosoft様、教えていただきありがとうございます。 UIViewではなくUIWindowを使うと良いのですね。 試しに、以下のようにUIWindowを作成して、そこにUIViewを追加するようにしてみました。UIViewControllerの派生クラス内で、PrezenforWindowを作成しています。 この場合でもPrezenforWindowのsendEvent()には来てくれるのですが、UIViewControllerの派生クラスのtouchesBegan()などには来てくれませんでした。 教えていただいたサンプルアプリも確認しましたが、解決できませんでした。 もしお分かりになりましたら、ご教示のほどお願い申し上げます。 ----------------------------------------------- // プレゼンウィンドウ // -プレゼン用にポインタを表示するウィンドウ class PrezenforWindow: UIWindow { var PrezenView: PrezenforView! override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = UIColor.clear self.layer.position = CGPoint(x: frame.width/2, y: frame.height/2) self.isHidden = false self.makeKeyAndVisible() // windowを表示 // ビューの追加 PrezenView = PrezenforView(frame: frame) PrezenView.backgroundColor = UIColor.clear PrezenView.isHidden = false self.addSubview(PrezenView) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func sendEvent(_ event: UIEvent) { if PrezenView != nil{ if event.type == .touches{ if let alltouches = event.allTouches{ var arPt:Array<CGPoint> = [] for touch in alltouches{ switch touch.phase{ case .began,.moved: let location = touch.location(in: self) arPt.append(location) break default: break } } PrezenView.Setptarray(array: arPt) PrezenView.setNeedsDisplay() // 描画更新 } } } super.sendEvent(event) } } // プレゼンビュー // -プレゼン用にポインタを表示するビュー class PrezenforView: UIView { var ptarray:Array<CGPoint> = [] override func draw(_ rect: CGRect) { for i in 0..<ptarray.count { // 円の描画 let circle = UIBezierPath(arcCenter: CGPoint(x: ptarray[i].x, y: ptarray[i].y), radius:18, startAngle: CGFloat(Double.pi*2.0*0.0/360.0), endAngle:CGFloat(Double.pi*2.0*360.0/360.0), clockwise: true) let circleColor = UIColor(red:0, green:0, blue:1, alpha:0.6) circleColor.setStroke() // 線の色を設定 circleColor.setFill() // 内側塗りつぶし色の設定 circle.fill() // 内側塗りつぶし circle.lineWidth = 0 circle.stroke() } } // 座標配列の設定 func Setptarray(array: Array<CGPoint>){ ptarray = array } }
takabosoft

2019/06/12 05:12

私が提示したものはタッチイベントを大本で検出する方法であって、どこかの階層のビューのtouchesBegan()にイベントが到達するようになるというものではありませんよ?
msa_winnie

2019/06/12 07:02

takabosoft様、お返事ありがとうございます。 すみません。よくわかっていませんでした。 以下のように試してみましたが、あっていますでしょうか。 -- おおもとのwindowをカスタムwindowに変更したところ、カスタムwindow内のsendEvent()とviewcontroller内のtouchesBegan()へのイベントを受け取ることができました。 ------------------ class AppDelegate: UIResponder, UIApplicationDelegate { var window: PrezenforWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let first: ViewController = ViewController() NaviController = NavigationController(rootViewController: first) self.window = PrezenforWindow(frame: UIScreen.main.bounds) self.window?.rootViewController = NaviController self.window?.makeKeyAndVisible() return true } ------------------ ただ、上のコメント(2019/06/12 11:49)で記載したコードのdraw(_ rect: CGRect)には入ってくるのですが、円が描画されない状況です。 bringSubviewToFront(PrezenforView)のように最前面にビューを移動すれば、円は描画されますが、このようにするとviewcontroller内のtouchesBegan()イベントを受け取れなくなってしまいます。 色々わからないことが多く、申し訳ありませんが、もしおわかりになりましたら、ご教示のほどお願い申し上げます。
takabosoft

2019/06/12 07:11

Storyboard使わないで実装したんですね、それでしたらウィンドウの差し替えはそれでよいと思います。 描画用のviewのisUserInteractionEnabledをfalseにすればタッチは透過すると思います。
msa_winnie

2019/06/12 07:22

takabosoft様、お返事ありがとうございます。 度々申し訳ありません。 タッチのイベントは取れているのですが、描画用のビューに円が表示されない状況です。(円の描画処理には入ってきています。) ビューの前後関係の問題でしょうか? それとも、カスタムwindow内にaddSubview()で描画用のビューを追加しているのがまずいのでしょうか? 初歩的な質問で申し訳ありませんが、よろしくお願いいたします。
takabosoft

2019/06/12 07:26

bringSubviewToFrontすれば出るとご自身でおっしゃっていますが・・・それをやった上でisUserInteractionEnabledをfalseにしてくださいと伝えたつもりです。
msa_winnie

2019/06/12 07:32

takabosoft様、お返事ありがとうございます。 色々混乱していました。すみません。 bringSubviewToFront()をした上で、isUserInteractionEnabledをfalseにしたところ、無事にやりたいことができました。 丁寧に教えていただきありがとうございました。 今後ともどうぞよろしくお願いいたします。
takabosoft

2019/06/12 07:36

どういたしまして????
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問