Swift NSTimer and IBOutlet Issue

2019-08-26 09:20发布

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?

2条回答
【Aperson】
2楼-- · 2019-08-26 09:37

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:

  1. Disable AutoLayout OR
  2. 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
    }
}
查看更多
在下西门庆
3楼-- · 2019-08-26 09:45

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.

查看更多
登录 后发表回答