Swift Timer.scheduledTimer()不起作用

时间:2017-05-03 09:01:45

标签: ios swift timer

我的swift应用程序中有两个视图。我正在执行如下的segue。

ViewController.swift -----------------> GameViewController.swift

加载GameViewController时,一个值数组也从ViewController.swift传递给GameViewController.swift

计时器应该在GameViewController.swift中初始化

我尝试初始化一个计时器并通过它调用一个方法,但它没有工作。

以下是我的代码段。

ViewController.swift

func signIn(difficultyLvl:String){
    let username = usernameTxt.text
    let password = passwordTxt.text

    let url = URL(string: "http://192.168.1.106/speed/scoreBoardController.php?username="+username!+"&password="+password!+"&action=SIGNIN")

    let task = URLSession.shared.dataTask(with: url!) {(data, response, error) in
        let isPassed = String(data: data!, encoding:.utf8)?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)

        var gameViewControllerParams = [Int: [String: String]]()
        gameViewControllerParams[0] = ["userId" : isPassed!]
        gameViewControllerParams[1] = ["difficultyLvl" : difficultyLvl]

        if(isPassed != "null"){
            self.performSegue(withIdentifier: "gotoGame", sender: gameViewControllerParams)
        }
    }

    task.resume()
}

GameViewController.swift

class GameViewController: UIViewController {

    var gameViewControllerParams = [Int: [String: String]]()

    override func viewDidLoad() {
        super.viewDidLoad()

        let _ = Timer.scheduledTimer(timeInterval: 1.0, target:self, selector: #selector(self.setCalculationLs), userInfo:nil,repeats: true)
    }

    func setCalculationLs(){
        print("Timing")
    }

}

1 个答案:

答案 0 :(得分:15)

计时器不在后台队列上工作(没有一些涉及创建运行循环或在现有运行循环上手动调度它的手段)。但是你绝不应该从主队列以外的任何东西发起任何UI更新。

因此,既然您从performSegue完成关闭(在后台队列中运行)调用URLSession,那么它实际上是从后台运行viewDidLoad队列也是。因此,计划定时器的尝试失败。要解决此问题,您必须手动将performSegue代码分发到主队列:

let task = URLSession.shared.dataTask(with: url!) { data, response, error in
    ...

    if isPassed != "null" {
        DispatchQueue.main.async {
            self.performSegue(withIdentifier: "gotoGame", sender: ...)
        }
    }
}

如果您不确定某些代码是否在主队列上运行,请参阅the documentation。或者您可以使用调度前提条件:

dispatchPrecondition(condition: .onQueue(.main))

如果您意外地从后台队列中调用了代码,那么它(在调试版本中)会停止应用程序。

与您当前的问题无关,但是除此之外,为了避免计时器和视图控制器之间存在强烈的引用周期,您通常希望保留对计时器的引用,以便您可以invalidate视图消失(例如,在viewDidAppear中创建计时器并在viewDidDisappear中删除它)。否则,你可以在被解雇后最终保留GameViewController,例如:

class GameViewController: UIViewController {

    weak var timer: Timer?

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        timer = Timer.scheduledTimer(timeInterval: 1.0, target:self, selector: #selector(setCalculationLs(_:)), userInfo: nil, repeats: true)
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)

        timer?.invalidate()
    }

    @objc func setCalculationLs(_ timer: Timer) {
        print("Tick")
    }
}

或者在iOS 10或更高版本中,您可以使用基于块的变体weak引用selfinvalidate deinit

class GameViewController: UIViewController {

    weak var timer: Timer?

    override func viewDidLoad() {
        super.viewDidLoad()

        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in
            self?.setCalculationLs()
        }
    }

    deinit {
        timer?.invalidate()
    }

    func setCalculationLs() {
        print("Tick")
    }

}