0

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

Anton O.
  • 653
  • 7
  • 27
  • I would not use CADisplayLink; instead create a button with your desired constraints and animate the constraint via UIView.animate... every time you add another button. This should move the newly created button always to the end of the desired target destination. Also you can then just add and forget the constraint if you don't need it afterwards. – Christian 'fuzi' Orgler Dec 30 '15 at 17:00
  • @Christian 'fuzi' Orgler I have been avoiding UIView.animate because I have seen that the movement is not the same speed the whole way (slower at the beginning and at the end). It's also harder I believe to detect the touch on the button(I can only do this if I use .presentationlayer I believe) – Anton O. Dec 30 '15 at 17:14
  • That sounds to me having an easing in your animation then, if you put UIViewAnimationOptionsCurveLInear then it's a linear animation :) Do you have to detect the touch on the button during movement? – Christian 'fuzi' Orgler Dec 30 '15 at 17:48
  • @Christian 'fuzi' Orgler Yes it would be important – Anton O. Dec 30 '15 at 18:55
  • 1
    But then I would not use a UIButton for this game-driven purpose. You could use UIViews stored and check on touchDidBegan etc. and trigger the action. Or use a game-specific framework like SceneKit etc. – Christian 'fuzi' Orgler Dec 30 '15 at 20:24
  • @Christian 'fuzi' Orgler Thanks for the suggestions, I appreciate it, but I think my app is too simple to be used on SceneKit or SpriteKit instead of the SingleViewApplication. I also believe that it would be easier to use a button since the button has a built in function that detects a touch. – Anton O. Dec 30 '15 at 20:44
  • @Christian 'fuzi' Orgler Can you put the code on how to detect a touch during an animation for a button please (maybe in the answer section or a link because when I tried what people proposed, it didn't work)? – Anton O. Jan 01 '16 at 15:58
  • done, hope this helps :) – Christian 'fuzi' Orgler Jan 05 '16 at 01:28

1 Answers1

0

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

Christian 'fuzi' Orgler
  • 1,682
  • 8
  • 27
  • 51