在swift中的其他viewcontroller中启动Timer

时间:2016-10-12 09:13:01

标签: ios swift xcode nstimer

我的计时器功能有问题。我点击一个btn (startBtn)时启动一个计时器,当我点击另一个btn (restBtn)时我将一个新的viewcontroller显示为一个popover现在它正确地启动了计时器以时间倒计时打开新的viewcontroller作为popover。

问题是当在新的viewcontroller中它命中0并且自己解散。它应该自动在第一个viewcontrolle中启动计时器。

以下是我在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?

非常感谢!

2 个答案:

答案 0 :(得分:1)

从我所看到的情况来看,您似乎正在初始化一个新的View Controller ......行:

let worskoutStartVC = workoutStartVC()

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

所以我看到你可能想要尝试的两件事:

  1. (可能是最佳选项)将对原始视图控制器的引用传递给第二个视图控制器,这样您就不必再次创建它。然后使用该引用重新创建计时器。 因为你现在正在做的是每次PopUpVC的计时器结束时创建一个新的视图控制器。 帖子末尾的详细信息

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

  3. 正如你的代码所说:

    // 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)
    

    注意:我尽力将类名调整为我认为它们来自您的代码,但有些事情可能需要调整。

答案 1 :(得分:0)

问题是timeLbl可能是一个隐式解包的可选项,在拥有它的视图控制器被释放后意外地为零。当您通过调用弹出窗口上的invalidate()来解除弹出窗口时,您应该使计时器无效,这样定时器回调就不会被它击中。

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