Search code examples
iosswiftuibuttonswift2cadisplaylink

Button stops moving when creating new button who moves


I'm creating a game where a button who is being created moves from one side of the screen to the other when I click a button called start. The problem is that when I click start before the button who was moving reaches its end point, it stops instead of continuing (and the another created button start moving like expected). Should I create a new CADisplayLink every time I click the start button? If so, how would I do that? Here's the code:

var button1 = UIButton()
var displayLink: CADisplayLink?
var startTime: CFAbsoluteTime?
let duration = 2.0
var leadingConstraint: NSLayoutConstraint!
var topConstraint: NSLayoutConstraint!
var l1 = false
@IBAction func start(sender: UIButton) {
    n1()
}
func n1() {
    l1 = false
    startTime = CFAbsoluteTimeGetCurrent()
    displayLink = CADisplayLink(target: self, selector: "handleDisplayLink:")
    displayLink?.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
}
func handleDisplayLink(displayLink: CADisplayLink) {
    if l1 == false { // so it doesn't randomize leading constraint twice
        button1 = createButton()
        let randomNumber = Int(arc4random_uniform(180) + 30)
        let elapsed = CFAbsoluteTimeGetCurrent() - startTime!
        var percentComplete = CGFloat(elapsed / duration)
        if percentComplete >= 1.0 {
            percentComplete = 1.0
            //   self.button1.removeFromSuperview()
            displayLink.invalidate()
            button1.hidden = true
        }
        leadingConstraint.constant = CGFloat(randomNumber)
        topConstraint.constant = 390 - 350 * percentComplete
        NSLayoutConstraint.activateConstraints([
            leadingConstraint,
            topConstraint,
            button1.widthAnchor.constraintEqualToConstant(75),
            button1.heightAnchor.constraintEqualToConstant(75)
            ])
        l1 = true
    }
    else{
        let elapsed = CFAbsoluteTimeGetCurrent() - startTime!
        var percentComplete = CGFloat(elapsed / duration)
        if percentComplete >= 1.0 {
            percentComplete = 1.0
            displayLink.invalidate()
            button1.hidden = true
        }
        topConstraint.constant = 390 - 350 * percentComplete
        NSLayoutConstraint.activateConstraints([
            leadingConstraint,
            topConstraint,
            button1.widthAnchor.constraintEqualToConstant(75),
            button1.heightAnchor.constraintEqualToConstant(75)
            ])
    }
}
func buttonPressed(sender: UIButton!) {
    button1.hidden = true
    displayLink?.invalidate()
}
func createButton() ->UIButton {
    let button = UIButton()
    button.setImage(UIImage(named: "BlueBall.png")!, forState: UIControlState.Normal)
    button.addTarget(self, action: "buttonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
    self.view.addSubview(button)
    button.translatesAutoresizingMaskIntoConstraints = false
    leadingConstraint = button.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor, constant: 0)
    topConstraint = button.topAnchor.constraintEqualToAnchor(view.topAnchor, constant: 0)
    NSLayoutConstraint.activateConstraints([
        leadingConstraint,
        topConstraint,
        button.widthAnchor.constraintEqualToConstant(75),
        button.heightAnchor.constraintEqualToConstant(75)
        ])
    return button
}

Please help. It would be really appreciated. Thanks in advance. Anton


Solution

  • Okay Anton O; as discussed I post an answer how to detect a touch on a moving UIView. This works for both, CAAnimation and UIView.animationWith..

    First I created an extension of CGRect, just for convenience:

    extension CGRect {
        init(position: CGPoint, size: CGSize) {
            self.origin = CGPoint(x: position.x - (size.width / 2.0), y: position.y - (size.height / 2.0))
            self.size = size
        }
    }
    

    Then I created two methods which create and move the view. You can adapt the code then to your needs. (I hold a global variable called nextView to keep reference to the view, can also be extended to an array of course)

    Create View:

    private func createView(index: Int) {
            nextView?.removeFromSuperview()
    
            nextView = UIView()
            nextView?.bounds = CGRect(x: 0, y: 0, width: 60, height: 60)
            nextView?.backgroundColor = UIColor.redColor()
            nextView?.center = CGPoint(x: 30, y: CGRectGetMidY(self.view.bounds))
    
            if let nextView = nextView {
                view.addSubview(nextView)
            }
        }
    

    Move View:

    private func moveView() {
            guard let nextView = nextView else {
                return
            }
    
            UIView.animateWithDuration(5.0) { () -> Void in
                nextView.center = CGPoint(x: CGRectGetMaxX(self.view.bounds) + 30, y: CGRectGetMidY(self.view.bounds))
            }
        }
    

    Detect Touch:

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
            super.touchesEnded(touches, withEvent: event)
    
            if let touch = touches.first, nextView = nextView {
                let touchRect = CGRect(position: touch.locationInView(self.view), size: CGSize(width: 20, height: 20))
    
                guard let viewPosition = nextView.layer.presentationLayer()?.position else {
                    return
                }
    
                let viewRect = CGRect(position: viewPosition, size: nextView.bounds.size)
    
                if CGRectIntersectsRect(touchRect, viewRect) {
                    print("👍 💯")
                } else {
                    print("👎")
                }
            }
        }
    

    You can extend the methods for your needs and also add some "performance" enhancing checks (like if a view is visible and move on or return right there in the touchesEnded method, etc.)