ludluck ludluck - 2 months ago 15
Swift Question

How do I touch moving buttons?

class ViewController: UIViewController {

let manImage = UIImage(named: "man.png")

let button = UIButton()

override func viewDidLoad() {
super.viewDidLoad()

button.setBackgroundImage(manImage, forState: .Normal)
button.addTarget(self, action: "activate", forControlEvents: .TouchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(button)
self.view.addConstraint(NSLayoutConstraint(
item: button,
attribute: .Leading,
relatedBy: .Equal,
toItem: view,
attribute: .Leading,
multiplier: 1,
constant: 0))
self.view.addConstraint(NSLayoutConstraint(
item: button,
attribute: .CenterY,
relatedBy: .Equal,
toItem: view,
attribute: .CenterY,
multiplier: 1,
constant: 0))

startAnimating()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func startAnimating() {
UIView.animateWithDuration(10, delay: 0, options: .CurveLinear, animations: {
self.button.frame.origin.x = self.button.frame.origin.x - 150}, completion: nil)
}

func activate() {
print(button.center.x)
}

}


How do I touch the button while it is moving? I read there is something about updating the hit testing, and using Quartz kit, but I did not understand the response because it just contained excerpts. Can someone please provide the simplest answer that involves my actual example code?

Thank you.

Edit: I would like to be able to touch the button where the image is currently, rather than where the button will end up.

Answer

You need to add .AllowUserInteraction as one of the options for the animation.

Replace this line

UIView.animateWithDuration(10, delay: 0, options: .CurveLinear, animations: {

With this

UIView.animateWithDuration(10, delay: 0, options: [.CurveLinear, .AllowUserInteraction], animations: {

This will let you click the button at the position that it will be at the end of the animation.

To click the button at the current state during the animation you need to override touchesBegan method and use the presentationLayer of the button to hitTest the touch position.
Add this code to your class:

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    guard let touch = touches.first else {
        return
    }
    let touchLocation = touch.locationInView(self.view)
    if self.button.layer.presentationLayer()?.hitTest(touchLocation) != nil {
        activate()
    }
}

And to access the current position of the button you can use the presentationLayer's frame, modify your activate() method like this:

func activate() {
    if let presentationLayer = button.layer.presentationLayer() {
        print(presentationLayer.frame.midX)
    } else {
        print(button.center.x)
    }
}

* It seems that as of iOS 10 there's no need to use touchesBegan and hitTest since .AllowUserInteraction is enough to let you click it at it's current state during animation.