Swiftアプリに2つのビューがあります。以下のようにセグエを実行しています。
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")
}
}
タイマーはバックグラウンドキューでは機能しません(実行ループを作成したり、既存の実行ループで手動でスケジュールしたりする手先の早業がなければ)。ただし、とにかく、メインキュー以外から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: ...)
}
}
}
一部のコードがメインキューで実行されているかどうかわからない場合は、 ドキュメント を参照してください。または、ディスパッチの前提条件を使用できます。
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
がself
を参照し、invalidate
が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")
}
}