在 swift 中的其他 viewcontroller 中启动计时器

Start Timer in other viewcontroller in swift

我的定时器功能有问题。我在单击 btn (startBtn) 时启动计时器,并在单击另一个 btn (restBtn)[=30 时显示新的 viewcontroller 作为弹出窗口=] 现在它正确启动计时器并打开新的 viewcontroller 作为弹出窗口,时间倒计时。

问题是在新的 viewcontroller 中它命中 0 并自行解雇。它应该在第一个视图控制器中自动启动计时器。

这是我在 mainVC 上的代码:

var timeLeft = 0
var myTimer: Timer!


@IBAction func startBtnPressed(_ sender: AnyObject) {
        timeLeft = 0
        myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(workoutStartVC.timerRunning), userInfo: nil, repeats: true)  
    }


@IBAction func restBtnPressed(_ sender: AnyObject) {
        print("rest mode button is pressed and i am showing a overlay right now with data count down")
        myTimer.invalidate()

        // show popup when user interacts with restbtn
        let popOpVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "sbPopUpID") as! PopUpVC
        self.addChildViewController(popOpVC)
        popOpVC.view.frame = self.view.frame
        popOpVC.view.backgroundColor = BLUE_COLOR.withAlphaComponent(0.75)
        self.view.addSubview(popOpVC.view)
        popOpVC.didMove(toParentViewController: self)

        timeLbl.text = "" 
    }

func timerRunning() {
        timeLeft = timeLeft + 1
        print("the timeLeft is: \(timeLeft)")

        **timeLbl.text = "\(timeLeft)"** (HERE I AM GETTING ON THE SECOND TIME WHEN DISMISSING THE POPUPVC A FOUND NILL)

        if timeLeft == 30 {
            myTimer.invalidate()
            timeLbl.text = ""
        }
    }

这是我在 PopUpVC 上的代码:

var timeLeft = 0
    var myTimer: Timer!

    override func viewDidLoad() {
        super.viewDidLoad()

        timeLeft = 10
        myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(PopUpVC.timerRunning), userInfo: nil, repeats: true)
    }


    func timerRunning() {
        timeLeft = timeLeft - 1
        restModeTimer.text = "\(timeLeft)"

        if timeLeft == 0 {
            myTimer.invalidate()
            restModeTimer.text = ""
            self.view.removeFromSuperview()

            let worskoutStartVC = workoutStartVC()
            worskoutStartVC.myTimer = nil
            worskoutStartVC.timeLeft = 0

            worskoutStartVC.myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: worskoutStartVC, selector: #selector(worskoutStartVC.timerRunning), userInfo: nil, repeats: true)
        }
    }  

谁能解释为什么我在下面的代码中加粗的部分展开选项值时得到的是 nil?

非常感谢!

问题是 timeLbl 可能是一个隐式展开的可选值,在拥有它的视图控制器被释放后意外地为 nil。当您通过调用 invalidate() 关闭弹出窗口时,您应该使计时器无效,这样计时器回调就不会在它不应该被命中后被命中。

此外,由于您已经注意到由于隐式解包可选而导致的崩溃,我建议您要么仔细处理该计时器回调中的所有情况,以便您 return 尽早 return在你不应该调用它的时候调用它(如果你碰巧有一个与计时器相关的错误,至少不会崩溃),或者使当前隐式解包的可选成为可选的,这样编译器会告诉你哪些地方容易受到攻击。

据我所见,您似乎正在初始化一个新的视图控制器...在行中:

let worskoutStartVC = workoutStartVC()

因此,当创建它时,标签的出口连接方式与从故事板初始化视图时的连接方式不同。

所以我看到您可能想尝试的 2 件事:

  1. (可能是最好的选择)将对原始视图控制器的引用传递给第二个视图控制器,这样您就不必再次创建它。然后您使用该引用来重新创建计时器。 因为您现在所做的实际上是每次 PopUpVC 的计时器结束时创建一个新的视图控制器。 更多详情在post

  2. 结尾
  3. (这将导致初始 VC 的多个副本,因此更多的是帮助理解正在发生的事情)您可以使用 UIStoryboard() 创建视图控制器的另一个副本就像您在上面的代码中所做的那样,应该正确连接插座。

正如您的代码所说:

// show popup when user interacts with restbtn
    let popOpVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "sbPopUpID") as! PopUpVC
    self.addChildViewController(popOpVC)

同样,选项 1 是 IMO 的最佳选择,尽管可能还有其他模式需要考虑。标签为零的原因是,当您使用 workoutStartVC().

创建视图时,它没有连接到标签对象

编辑:添加有关如何传递参考的信息:

PopUpVC 代码可能会更改为引用原始 VC:

var timeLeft = 0
var myTimer: Timer!
var parentVC: workoutStartVC?

override func viewDidLoad() {
    super.viewDidLoad()

    timeLeft = 10
    myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(PopUpVC.timerRunning), userInfo: nil, repeats: true)
}


func timerRunning() {
    timeLeft = timeLeft - 1
    restModeTimer.text = "\(timeLeft)"

    if timeLeft == 0 {
        myTimer.invalidate()
        restModeTimer.text = ""
        self.view.removeFromSuperview()

        if let worskoutStartVC = workoutStartVC {
            worskoutStartVC.myTimer = nil
            worskoutStartVC.timeLeft = 0

            worskoutStartVC.myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: worskoutStartVC, selector: #selector(worskoutStartVC.timerRunning), userInfo: nil, repeats: true)
        }
    }
}  

当您转换到此视图时,您必须设置此 属性: (片段)

        // show popup when user interacts with restbtn
        let popOpVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "sbPopUpID") as! PopUpVC
        self.addChildViewController(popOpVC)
        popOpVC.parentVC = self
        popOpVC.view.frame = self.view.frame
        popOpVC.view.backgroundColor = BLUE_COLOR.withAlphaComponent(0.75)
        self.view.addSubview(popOpVC.view)
        popOpVC.didMove(toParentViewController: self)

注意:已尽力将 class 名称调整为我认为它们来自您的代码的名称,但有些内容可能需要调整。