I'm using NSTimer to update an image's position on the screen to give the illusion of movement. It's a flappy bird clone I'm creating to learn Swift. Anyway, I'm also updating an outlet by displaying an incrementing Int as the score. However, every time I update the Int and the corresponding outlet, the images on the screen reset to their starting point (bird and tubes). Very odd. Has anyone else come across this? What am I doing wrong?
@IBOutlet weak var tube1: UIImageView!
@IBOutlet weak var bird1: UIImageView!
@IBOutlet weak var score: UILabel!
func startTimer() {
timer = NSTimer.scheduledTimerWithTimeInterval(0.02, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
func update() {
bird1.center=CGPointMake(50,bird1.center.y+CGFloat(num));
tube1.center=CGPointMake(tube1.center.x-CGFloat(num2),tube1.center.y);
if(tube1.center.x<(-40)){
tube1.center.x=screenWidth;//variable set outside this method
tube1.center.y=0;//will be random
updateMyScore();
}
}
func updateMyScore(){
myScore=++;
score.text="Score: \(myScore)"
}
The weird thing is if I comment out the score.text="Score:(myScore)" line everything works fine, but I'd like to have the score show on the screen. Any suggestions?
AutoLayout is running and resetting the position of your images. It is running because you are updating your UILabel
. There are 2 ways you can deal with this:
- Disable AutoLayout OR
- Store the positions of your tubes and pipes in properties and then set them in an override of
viewDidLayoutSubviews()
.
Here is how you might implement option #2:
@IBOutlet weak var tube1: UIImageView!
@IBOutlet weak var bird1: UIImageView!
@IBOutlet weak var score: UILabel!
var tube1center: CGPoint?
var bird1center: CGPoint?
func startTimer() {
timer = NSTimer.scheduledTimerWithTimeInterval(0.02, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
func update() {
bird1.center=CGPointMake(50,bird1.center.y+CGFloat(num));
tube1.center=CGPointMake(tube1.center.x-CGFloat(num2),tube1.center.y);
if(tube1.center.x<(-40)){
tube1.center.x=screenWidth;//variable set outside this method
tube1.center.y=0;//will be random
updateMyScore();
}
bird1center = bird1.center
tube1center = tube1.center
}
func updateMyScore(){
myScore=++;
score.text="Score: \(myScore)"
}
override func viewDidLayoutSubviews() {
if let newcenter = tube1center {
tube1.center = newcenter
}
if let newcenter = bird1center {
bird1.center = newcenter
}
}
Yes, this is well known behavior of auto layout. If you do anything that causes the auto layout engine to reapply the constraints to the view (such as updating the score), any adjustments to layout-related properties will be lost, reset to their values as dictated by the constraints.
As Vacawama pointed out, you could turn off auto layout. Alternatively, rather than manually changing the frame
in your timer routine, you could instead create an IBOutlet
for the constraints, themselves, and you can them modify the constraint's constant
property and then call layoutIfNeeded
. That way, if you happen to do something else that triggers the constraints, everything will be fine.