如何在动画过程中检测UIImageView上的点击?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在动画过程中检测UIImageView上的点击?相关的知识,希望对你有一定的参考价值。

我尝试在动画过程中检测UIImageView上的点击,但它不起作用。

我做什么(快速4):

通过StoryBoard添加了UIImageView:

@IBOutlet weak var myImageView: UIImageView!

做动画:

override func viewWillAppear (_ animated: Bool) {
        super.viewWillAppear (animated)

        myImageView.center.y + = view.bounds.height

    }


    override func viewDidAppear (_ animated: Bool) {
        super.viewDidAppear (animated)

        UIView.animate (withDuration: 10, delay: 0, options: [.repeat, .autoreverse, .allowUserInteraction], animations: {
            self.myImageView.center.y - = self.view.bounds.height
        })
    }

尝试检测水龙头:

override func viewDidLoad () {
        super.viewDidLoad ()

        let gestureSwift2AndHigher = UITapGestureRecognizer (target: self, action: #selector (self.actionUITapGestureRecognizer))
        myImageView.isUserInteractionEnabled = true
        myImageView.addGestureRecognizer (gestureSwift2AndHigher)

    }


    @objc func actionUITapGestureRecognizer () {

        print ("actionUITapGestureRecognizer - works!")

    }

在投票提出问题之前,请确保没有正常的问题答案,这对初学者来说是可以理解的,并且在版本2以上的swift中写的,所以我不能将它们用于我的案例。

研究这个问题,我意识到有必要调整框架!但这对我来说仍然很难。请告诉我,我需要在下面的代码中添加或更改内容。

谢谢您的帮助。

class ExampleViewController: UIViewController {

    @IBOutlet weak var myImageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // action by tap
        let gestureSwift2AndHigher = UITapGestureRecognizer(target: self, action:  #selector (self.actionUITapGestureRecognizer))
        myImageView.isUserInteractionEnabled = true
        myImageView.addGestureRecognizer(gestureSwift2AndHigher)

    }

    // action by tap
    @objc func actionUITapGestureRecognizer (){

        print("actionUITapGestureRecognizer - works!") // !!! IT IS DOES NOT WORK !!!

    }

    // hide UIImageView before appear
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        myImageView.center.y += view.bounds.height

    }

    // show UIImageView after appear with animation
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        UIView.animate(withDuration: 10, delay: 0, options: [.repeat, .autoreverse, .allowUserInteraction], animations: {
            self.myImageView.center.y -= self.view.bounds.height
        })
    }
}
答案

你不能使用UITapGestureRecognizer做你想要的,因为它使用基于frame的检测,并通过检查其框架来检测你的视图中是否有触摸。

问题是,动画在动画开始之前已经设置了视图的最终帧。然后它会在您再次显示真实视图之前将视图的快照设置为动画。

因此,如果您要点击动画的最终位置,即使您的视图看起来不像它,您也会看到您的点按手势被点击。您可以在下图中看到:

https://i.imgur.com/Wl9WRfV.png

(左侧是视图层次结构检查器)..(右侧是模拟器动画)。

要解决点击问题,您可以尝试一些粗略的代码(但有效):

import UIKit

protocol AnimationTouchDelegate {
    func onViewTapped(view: UIView)
}

protocol AniTouchable {
    var animationTouchDelegate: AnimationTouchDelegate? {
        get
        set
    }
}

extension UIView : AniTouchable {
    private struct Internal {
        static var key: String = "AniTouchable"
    }

    private class func getAllSubviews<T: UIView>(view: UIView) -> [T] {
        return view.subviews.flatMap { subView -> [T] in
            var result = getAllSubviews(view: subView) as [T]
            if let view = subView as? T {
                result.append(view)
            }
            return result
        }
    }

    private func getAllSubviews<T: UIView>() -> [T] {
        return UIView.getAllSubviews(view: self) as [T]
    }

    var animationTouchDelegate: AnimationTouchDelegate? {
        get {
            return objc_getAssociatedObject(self, &Internal.key) as? AnimationTouchDelegate
        }

        set {
            objc_setAssociatedObject(self, &Internal.key, newValue, .OBJC_ASSOCIATION_ASSIGN)
        }
    }


    override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let touchLocation = touch.location(in: self)

        var didTouch: Bool = false
        let views = self.getAllSubviews() as [UIView]
        for view in views {
            if view.layer.presentation()?.hitTest(touchLocation) != nil {
                if let delegate = view.animationTouchDelegate {
                    didTouch = true
                    delegate.onViewTapped(view: view)
                }
            }
        }

        if !didTouch {
            super.touchesBegan(touches, with: event)
        }
    }
}

class ViewController : UIViewController, AnimationTouchDelegate {

    @IBOutlet weak var myImageView: UIImageView!

    deinit {
        self.myImageView.animationTouchDelegate = nil
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        self.myImageView.isUserInteractionEnabled = true
        self.myImageView.animationTouchDelegate = self
    }

    func onViewTapped(view: UIView) {
        print("Works!")
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        myImageView.center.y += view.bounds.height
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        UIView.animate(withDuration: 5, delay: 0, options: [.repeat, .autoreverse, .allowUserInteraction], animations: {
            self.myImageView.center.y -= self.view.bounds.height
        })
    }
}

它通过覆盖UIView上的touchesBegan,然后检查是否有任何触摸落在该视图内。

一个更好的方法是在UIViewController中做它而不是..

import UIKit

protocol AnimationTouchDelegate : class {
    func onViewTapped(view: UIView)
}

extension UIView {
    private class func getAllSubviews<T: UIView>(view: UIView) -> [T] {
        return view.subviews.flatMap { subView -> [T] in
            var result = getAllSubviews(view: subView) as [T]
            if let view = subView as? T {
                result.append(view)
            }
            return result
        }
    }

    func getAllSubviews<T: UIView>() -> [T] {
        return UIView.getAllSubviews(view: self) as [T]
    }
}

class ViewController : UIViewController, AnimationTouchDelegate {

    @IBOutlet weak var myImageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.myImageView.isUserInteractionEnabled = true
    }

    func onViewTapped(view: UIView) {
        print("Works!")
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        myImageView.center.y += view.bounds.height
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        UIView.animate(withDuration: 5, delay: 0, options: [.repeat, .autoreverse, .allowUserInteraction], animations: {
            self.myImageView.center.y -= self.view.bounds.height
        })
    }

    override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let touchLocation = touch.location(in: self.view)

        var didTouch: Bool = false
        for view in self.view.getAllSubviews() {
            if view.isUserInteractionEnabled && !view.isHidden && view.alpha > 0.0 && view.layer.presentation()?.hitTest(touchLocation) != nil {

                didTouch = true
                self.onViewTapped(view: view)
            }
        }

        if !didTouch {
            super.touchesBegan(touches, with: event)
        }
    }
}

以上是关于如何在动画过程中检测UIImageView上的点击?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中检测 UIImageView 上的 UIImageView 更改或 KVO 炒锅

如何在点击手势上为 UIImageView 设置动画?

ios uiimageview点击手势在动画期间不起作用

iOS:UIImageView 不响应动画之间的点击手势

如何在动画完成时在 imageview 中启用交互

在 CustomCell 中检测 UIImageView 上的触摸