31520


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

今現在、アニメーション中の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]


CC BY-SA 3.0
2016-12-28T18:14:28.000
31506
2016-12-29T16:35:33.443
2016-12-29T13:04:33.197
76
20049
question
swift
アニメーション中のビューをタップしてイベント処理


質問にある文章だけでは問題点を特定できませんので的外れかもしれませんが、UIViewを使ってアニメーションさせているのであれば、
`animate(withDuration:delay:options:animations:completion:)` の `options` で
`.allowUserInteraction` を指定してみてはどうでしょうか？

UIView - UIKit | Apple Developer Documentation  
<https://developer.apple.com/reference/uikit/uiview>

animate(withDuration:delay:options:animations:completion:) - UIView | Apple
Developer Documentation  
<https://developer.apple.com/reference/uikit/uiview/1622451-animate>


CC BY-SA 3.0
2016-12-29T00:21:56.897
31508
2016-12-29T00:21:56.897
7459
31506
answer


**注** :
あまりうまくいっているとは言えない例です。一応解決のための糸口くらいにはなるかもしれないと言うことで回答の形で書かせてもらっているということでご承知おきください。

* * *

See.Ku
さんがご紹介くださっているように、`UIView`の`isUserInteractionEnabled`プロパティはアニメーションの最中は無効化されてしまいます。肝心な部分だけ抜き出すと:

[isUserInteractionEnabled](https://developer.apple.com/reference/uikit/uiview/1622577-isuserinteractionenabled)

> During an animation, user interactions are temporarily disabled for  
>  all views involved in the animation, regardless of the value in this  
>  property.
>
> アニメーションの最中は、このプロパティの値に関わらず、アニメーションに関わる全てのviewにおいてユーザインタラクションが一時的に無効化されます。

ただ、その続きにこうあります:

> You can disable this behavior by specifying the allowUserInteraction  
>  option when configuring the animation.
>
> この動作はアニメーションを構成する際のoptionに`allowUserInteraction`を指定することで無効化できます。

* * *

と言うわけでSee.Ku
さんの回答にある`options:`パラメータ付きの`animate`メソッドで`allowUserInteraction`を指定してみるように修正したものがこちらです。

[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 //<-(0)
    
            let _selector = #selector(tap) //<-(1)
            let _gesture = UITapGestureRecognizer(target: self, action: _selector) //<-(2)
            green_view.addGestureRecognizer(_gesture)
        }
    
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            //(3)
            UIView.animate(withDuration: 5.0, delay: 1.0, options: .allowUserInteraction, animations: {
                self.green_view.center.y += self.view.bounds.size.width
            }){_ in
            }
        }
    
        @IBAction func tap(_ gestureRecognizer: UIGestureRecognizer) { //<-(4)
            if green_view.backgroundColor == .green {
                green_view.backgroundColor = .red
            } else {
                green_view.backgroundColor = .green
            }
        }
    }
    
[/code]

一番肝心な(3)の他、致命的なもの、そうでないもの含めて若干の修正を施してあります。`//<-`でマーキングしてありますが、解決に至るコードではないので、詳しいことは置いておきます。

* * *

で、こちらで実際に上記のコードを試した結果ですが、アニメーションの途中でタップに反応するようにはなるものの、アニメーション終了寸前で、とても使い物にはならないと言う感じでした。(低スペックのMac上でsimulatorを動かすと時々極端にレスポンスが悪くなるので、実機でも試してみましたが、ほとんど同じでした。)全く反応しないではテストにならないので、コード中にあるようにディレイを入れたり、継続時間を5秒に伸ばしたりしたのですが、ダメでした。

ご自身で書かれている「反応領域を広げるとできる」と言う記事の出所がわかれば何がしかの改善ができるかもしれません。とりあえず中間報告ということで。


CC BY-SA 3.0
2016-12-29T05:03:37.880
31516
2016-12-29T05:03:37.880
13972
31506
answer


`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]


CC BY-SA 3.0
2016-12-29T07:26:29.233
31520
2016-12-29T16:35:33.443
2016-12-29T16:35:33.443
5519
5519
31506
answer