アニメーション中のビューをタップしてイベント処理


タイトルの処理をしたいのですができずに困っていますご教授願います。

今現在、アニメーション中のviewをタップすることによりビューのカラーを変更するといった単純なプログラムを組んでいます。

アニメーションをさせることとUIGestureReconizerでのタップ処理はできましたが、アニメーション中にタップしても反応しません。

この解決策を教えていただきたいです。

検索してみると反応領域を広げるとできると書いてあったのですがイマイチイメージがつかめないのと実装ができません。

追記。。  
すみません大切なところが抜けていましたお願いします。

[code]

    @IBOutlet weak var green_view: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        green_view.isUserInteractionEnabled = true
        green_view.center.y -= view.center.y
    
        let _selector = Selector(("tap:"))
    
        let _gesture:UIGestureRecognizer = UIGestureRecognizer(target: self, action: _selector)
    
        green_view.addGestureRecognizer(_gesture)
    
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        UIView.animate(withDuration: 2.0, animations: {()->Void in
            self.green_view.center.y += self.view.bounds.size.width}){_ in
        }
    }
    
    
    @IBAction func Object_Gesture(_ sender: Any) {
    
        if(green_view.backgroundColor == .green){
    
            green_view.backgroundColor = UIColor.red
    
        }else{
    
            green_view.backgroundColor = UIColor.green
        }
    }
    
[/code]




`UIView.animate()`メソッドでアニメーションさせているビューの状態は、実際にはこのメソッドが実行された直後にすでに最終地点に移動しています。ビューの座標などブロックの中で行われた変更はアニメーションの開始前にすでに適用されていて、視覚効果だけがパラメータに従ってCore
Animationによって実行されるというような感覚です。

> アニメーションの途中でタップに反応するようにはなるものの、アニメーション終了寸前で、とても使い物にはならないと言う感じでした。

このようになる原因は処理能力の問題ではなく、上記のような挙動により、ビューはアニメーションの実行前にすでに移動が完了しているため、アニメーションの終了寸前では、実際のビューが存在するところをタップすることになるのでタップが検知されているということです。  
試しにアニメーション中の位置にかかわらず、移動先の何もないあたりをタップしてみてください。おそらく、リアルタイムにタップが検知できると思います。

ではアニメーション中の位置に応じてタップを検出するのはどうすればいいかというと、`presentationLayer`オブジェクトを利用します。タップされた座標と、`presentationLayer`の位置を比較して、座標が`presentationLayer`に含まれていればアニメーション中のビューがタップされたとみなします。

上記の通り、ビューそのものは実際には1フレームずつ移動したりはしていないので、対象のビューにGestureRecognizerを設定しても意図した通りには動きません。対象のビューを格納しているビューにGestureRecognizerを設定します。  
下記の例では`self.view`にGestureRecognizerを設定して、画面全体のタップを取得できるようにします。  
そして、タップされた座標と、`presentationLayer`の位置を調べて、座標が`presentationLayer`に含まれるなら、背景色を変えるという処理をします。  
（GestureRecognizerを`green_view`ではなく`view`に設定しているところと、`tap(_:)`メソッドを元のコードから変更しています。）

[code]

    class ViewController: UIViewController {
    
        @IBOutlet weak var green_view: UIView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            green_view.isUserInteractionEnabled = true
            green_view.center.y -= view.center.y
            green_view.backgroundColor = .green
    
            let _selector = #selector(tap(_:))
            let _gesture = UITapGestureRecognizer(target: self, action: _selector)
            view.addGestureRecognizer(_gesture)
        }
    
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            UIView.animate(withDuration: 5.0, delay: 1.0, options: [.allowUserInteraction], animations: {
                self.green_view.center.y += self.view.bounds.size.width
            })
        }
    
        func tap(_ gestureRecognizer: UIGestureRecognizer) {
            let point = gestureRecognizer.location(in: view)
            if let presentation = green_view.layer.presentation(),
                let _ = presentation.hitTest(point) {
                if green_view.backgroundColor == .green {
                    green_view.backgroundColor = .red
                } else {
                    green_view.backgroundColor = .green
                }
            }
        }
    }
    
[/code]

別解として、CoreAnimationによるレイヤーアニメーションを使わずに、タイマーなど利用して実際にビューを1ポイントずつ動かすという方法もあります。これなら実際にビューがアニメーションの途中でも見た目の位置に存在するので、タップなどは意図した通りに動作します。

[code]

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        Timer.scheduledTimer(withTimeInterval: 0.016, repeats: true) { (timer) in
            self.green_view.center.y += 1.0
            if self.green_view.center.y >= self.view.bounds.size.width {
                timer.invalidate()
            }
        }
    }
    
[/code]




`UIView.animate()`メソッドでアニメーションさせているビューの状態は、実際にはこのメソッドが実行された直後にすでに最終地点に移動しています。ビューの座標などブロックの中で行われた変更はアニメーションの開始前にすでに適用されていて、視覚効果だけがパラメータに従ってCore
Animationによって実行されるというような感覚です。

> アニメーションの途中でタップに反応するようにはなるものの、アニメーション終了寸前で、とても使い物にはならないと言う感じでした。

このようになる原因は処理能力の問題ではなく、上記のような挙動により、ビューはアニメーションの実行前にすでに移動が完了しているため、アニメーションの終了寸前では、実際のビューが存在するところをタップすることになるのでタップが検知されているということです。  
試しにアニメーション中の位置にかかわらず、移動先の何もないあたりをタップしてみてください。おそらく、リアルタイムにタップが検知できると思います。

ではアニメーション中の位置に応じてタップを検出するのはどうすればいいかというと、`presentationLayer`オブジェクトを利用します。タップされた座標と、`presentationLayer`の位置を比較して、座標が`presentationLayer`に含まれていればアニメーション中のビューがタップされたとみなします。

上記の通り、ビューそのものは実際には1フレームずつ移動したりはしていないので、対象のビューにGestureRecognizerを設定しても意図した通りには動きません。対象のビューを格納しているビューにGestureRecognizerを設定します。  
下記の例では`self.view`にGestureRecognizerを設定して、画面全体のタップを取得できるようにします。  
そして、タップされた座標と、`presentationLayer`の位置を調べて、座標が`presentationLayer`に含まれるなら、背景色を変えるという処理をします。  
（GestureRecognizerを`green_view`ではなく`view`に設定しているところと、`tap(_:)`メソッドを元のコードから変更しています。）

[code]

    class ViewController: UIViewController {
    
        @IBOutlet weak var green_view: UIView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            green_view.isUserInteractionEnabled = true
            green_view.center.y -= view.center.y
            green_view.backgroundColor = .green
    
            let _selector = #selector(tap(_:))
            let _gesture = UITapGestureRecognizer(target: self, action: _selector)
            view.addGestureRecognizer(_gesture)
        }
    
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            UIView.animate(withDuration: 5.0, delay: 1.0, options: [.allowUserInteraction], animations: {
                self.green_view.center.y += self.view.bounds.size.width
            })
        }
    
        func tap(_ gestureRecognizer: UIGestureRecognizer) {
            let point = gestureRecognizer.location(in: view)
            if let presentation = green_view.layer.presentation(),
                let _ = presentation.hitTest(point) {
                if green_view.backgroundColor == .green {
                    green_view.backgroundColor = .red
                } else {
                    green_view.backgroundColor = .green
                }
            }
        }
    }
    
[/code]

別解として、CoreAnimationによるレイヤーアニメーションを使わずに、タイマーなど利用して実際にビューを1ポイントずつ動かすという方法もあります。これなら実際にビューがアニメーションの途中でも見た目の位置に存在するので、タップなどは意図した通りに動作します。

[code]

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        Timer.scheduledTimer(withTimeInterval: 0.016, repeats: true) { (timer) in
            self.green_view.center.y += 1.0
            if self.green_view.center.y >= self.view.bounds.size.width {
                timer.invalidate()
            }
        }
    }
    
[/code]

