如何在动画制作过程中检测UIImageView上的点按?

时间:2017-12-30 13:22:37

标签: ios uiimageview core-animation swift4

我尝试在动画过程中检测到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版以上快速写,所以我不能将它们用于我的案例。

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

感谢您的帮助。

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
        })
    }
}

2 个答案:

答案 0 :(得分:0)

您无法使用UITapGestureRecognizer执行所需操作,因为它使用基于frame的检测,并通过检查其框架来检测视图内是否有触摸。

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

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

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)
        }
    }
}

答案 1 :(得分:0)

要检测移动(动画)视图上的触摸,只需使用表示层覆盖hitTest

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

    return (layer.presentation()!.frame)
                      .contains(self.convert(point, to: superview!)) ? self : nil
}

在当前示例中

  1. 它可与所有手势识别器一起使用

  2. 请勿在视图控制器级别

  3. 修改任何框架或其他任何内容
  4. 仅将视图本身子类化,在上面添加覆盖

别自然地忘记,如果要在抓取项目后停止动画,请使用yourPropertyAnimator?.stopAnimation(true) , yourPropertyAnimator = nil

(在视图控制器中)进行操作。