I'm new to Swift and I'm trying to implement a simple game. In this game, once the view is loaded I want periodic animations to happen.
The problem is that I try to animate my buttons with, for instance, button.frame.origin.x += 50
, but instead of moving it from the origin to 50px right, it appears at button.frame.origin.x - 50 and goes to its (initial) position.
Funnily enough, at one point I show an AlertDialog
to the user, and after it is shown the animation starts to happen as I expected.
My problem is the same of this topic, but the accepted answer just didn't solve it for me.
Any thoughts?
Edit: Digging into the code and testing a lot I found out the method where I show the AlertDialog also happens to invalidate my timer. I have two timers: one to update the UI time (a TextField), and the other to perform the animation. Both are scheduling a task. From what I read here, it is not possible to have two timers like that.
A timer object can be registered in only one run loop at a time, although it can be added to multiple run loop modes within that run loop.
Hence, the obvious solution would be to merge the two selectors (functions) into one, so the timer would work for both tasks. However, if I try to update the UI AND perform animations, the animations don't work as expected anymore. I'm just lost on this problem and can't make both things work.
These are my important pieces of code:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
...
resetOrCreateGame()
}
func resetOrCreateGame() {
// Do stuff to initialize values
timerToRotate = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "rotateBlocks", userInfo: nil, repeats: true)
}
func rotateBlocks() {
// Calculate positions to rotate
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
// Try to update a timer in the UI
//self.timeLeft.text = String(self.seconds)
for i in 0 ... 8 {
println("\(i) before \(self.buttons[i].frame.origin.x) \(self.buttons[i].frame.origin.y)")
self.buttons[i].frame.origin.x = self.positions[self.indicesToRotate[i]].x
self.buttons[i].frame.origin.y = self.positions[self.indicesToRotate[i]].y
println("\(i) after \(self.buttons[i].frame.origin.x) \(self.buttons[i].frame.origin.y)")
}
}, completion: { _ in
println("completed")
})
If I leave the line self.timeLeft.text = String(self.seconds)
commented, the animations work fine. No matter how I try to update the timeLeft, if I do so it screws my animations. I tried to update it in a separate thread, dispatch it to the main thread, or even update it inside the animations closure: it just doesn't work.
Try using
SKAction
s to animate.Set up an
SKAction
, then on theSpriteKit node
you want to animate (replace "node
" with the name of the node), and call itaction
.Then, call the simple method:
[node runAction:action];
For example, if you want to set up an
SKAction
to move a node titledbutton
50 pixels to the right over a timespan of 3 seconds...Heck, if I don't run an action more than once, I'd simply do this:
And finally, you can use the
runAction:completion:
method to not only run yourSKAction
, but to call an Objective-Cblock
after it's finished, like in this coding example:And, to non-complicate your code if you want to run a sequence of actions on a node (move a button 50 pixels to the right called
button
over 3 seconds, then fade it out over 1 second), you can program a sequence of actions into one action, like so:You CAN run another action in the
completion
block, but the method of using asequence
cleans up code.Finally, I found the solution! The problem for some reason was Auto Layout. After disabling it everything worked as expected.
Hope it helps someone in the future!