2017-05-03 76 views
5

Mam dwa widoki w mojej szybkiej aplikacji. Wykonuję segue, jak poniżej.Swift Timer.scheduledTimer() nie działa

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

podczas ładowania GameViewController tablicę wartości również przekazany do GameViewController.swift z ViewController.swift

czasomierz powinien zostać zainicjowany w GameViewController.swift

próbowałem zainicjować licznik i wywołać metodę przez niego, ale to nie działa.

Poniżej znajdują się moje fragmenty kodu.

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") 
    } 

} 
+0

Uważam, że jest to problem z ustawianiem obiektu docelowego w stoperze. Wszelkie sugestie. Dzięki –

+0

Nie wysyłasz tablicy do 'GameViewController', zrób to w' przygotować (dla: nadawca:) '. parametr 'nadawca' powinien być' self' – paper1111

+0

Jaki dokładnie jest problem? Czy załadowany jest GameViewController? Czy wywoływana jest metoda viewDidLoad()? Jak przebiega przekazywanie tablicy związane z problemem timera? - Generalnie, kod timera powinien zadziałać. –

Odpowiedz

10

Timers nie działają w tle (bez kolejek niektóre kuglarstwo udziałem tworząc pętle biegu lub ręcznie planowanie go na istniejącej perspektywie pętla). Ale w żadnym wypadku nie powinieneś inicjować żadnej aktualizacji interfejsu użytkownika z niczego poza główną kolejką.

Tak, ponieważ dzwonisz performSegue z zamknięciem URLSession zakończenia (który działa na kolejce w tle), to faktycznie działa viewDidLoad z kolejki w tle, zbyt. Tak więc próba zaprogramowania timera kończy się niepowodzeniem. Aby obejść ten problem, trzeba ręcznie wysyła kod performSegue do głównej kolejki:

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

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

Jeśli nie masz pewności, czy jakiś kod działa na głównym kolejce czy nie, patrz the documentation. Albo można użyć warunek wysyłki:

dispatchPrecondition(condition: .onQueue(.main)) 

ten sposób to będzie (w debug buduje) zatrzymać aplikację, jeśli już przypadkowo wywołany kod z kolejki w tle.


niezwiązane z aktualnego problemu, ale jak na bok, aby uniknąć silnego cyklu odniesienia pomiędzy zegarem a kontrolerem widoku, zazwyczaj chcą zachować odniesienie do zegara, tak aby można go invalidate gdy widok zniknie (np. stwórz timer w viewDidAppear i usuń go w viewDidDisappear). W przeciwnym razie może się to skończyć zachowując GameViewController po to został zwolniony, np:

class GameViewController: UIViewController { 

    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() 
    } 

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

Albo w iOS 10, można skorzystać z wariantu bloku opartego o weak odniesieniu do self i invalidate w deinit:

class GameViewController: UIViewController { 

    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") 
    } 

} 
+0

Oh! zadziałało. Dziękuję bardzo za miłe wsparcie. Będzie to bardzo pomocne dla nowych, szybkich uczniów, takich jak ja. Ponadto spróbuję zmienić logikę aplikacji, aby nie inicjować aktualizacji interfejsu użytkownika z niczego innego niż główna kolejka. Jeszcze raz dziękuję –

+0

Rob, wolisz metodę [słabego self] lub viewDidLoad/viewDidDisappear? Dzięki. –

+0

Wolę wzór '[słabe ja]', ale czasami trzeba używać starego interfejsu API, ponieważ obsługujesz stare wersje iOS. A jeśli utknąłeś ze starym API, technicznie, podczas gdy mój przykład to 'viewDidLoad' /' viewDidDisappear', prawdopodobnie sugerowałbym 'viewDidAppear' oraz' viewDidDisappear' (ponieważ zawsze będą one zrównoważone, podczas gdy 'viewDidLoad' może nie być równoważone wywołaniami 'viewDidDisappear', szczególnie jeśli" prezentujesz "inny kontroler widoku modalnie). Użyłem tylko 'viewDidLoad' powyżej, ponieważ tam właśnie OP tworzył swój timer w pierwotnym pytaniu. – Rob