Swift:功能结束时的反馈

时间:2017-05-06 06:17:42

标签: ios swift

假设我有一个ViewController A和一个B类。

当我在A中按下某个按钮时,它会调用一个调用函数B.foo()的IBAction,它返回一个Int

B.foo()需要8~10秒才能完成,当它运行时我想在A上放一个Loading...动画,当B.foo()结束时,动画就会停止。 / p>

我该怎么做?这是我希望的伪代码示例:

@IBAction func buttonPressed(_ sender: UIButton){
    UIView.animate(blablabla......)
    DO({
        self.answer = B.foo()
    }, andWhenItFinishesDo: {
        self.someone.layer.removeAllAnimation()
    })
}

2 个答案:

答案 0 :(得分:1)

这是一个非常常见的问题。解决问题的一种方法是使用不同的队列(您可以将它们视为可以并行发生的工作线)。

基本的想法是,按下按钮后,您将显示您的加载指示符并将长时间工作“分派”到辅助队列,该队列将在后台运行并完成工作。这可确保您的主队列在工作发生时不会阻塞,并且用户界面保持响应。

现在的诀窍是,您希望在完成长时间工作后收到通知,以便您可以停止显示加载指示符(并可能更多)。

虽然您实际上可以使用某种通知系统,但还有其他一些更合适的方法。如果您可以告诉长时间运行的功能使用您提供的代码专门给您回电,那实际上会更方便。

这将是“完成处理程序”或“回调”的基本概念。

整个事情看起来像那样:

// Some made up class here
class B {
   // This is only static because I do not have an instance of B around. 
   static func foo(completion: @escaping (Int) -> Void ) {
      // The method now does all of its work on a background queue and returns immediately
      DispatchQueue.global(qos: .background).async {
         // In the background this may take as long as it wants
         let result = longCalculation()
         // VERY important. The caller of this function might have a certain 
         // expectation about on which queue the completion handler runs. 
         // Here I just use the main queue because this is relatively safe.
         // You could let the caller provide a queue in the function 
         // parameters and use it here
         DispatchQueue.main.async {
            // The completion handler is a function that takes an Int. 
            // That is exactly what you are providing here
            completion(result)
         }
      }
   }
}

@IBAction func buttonPressed(_ sender: UIButton){
   self.showLoadingIndicator()
   // The foo function now takes a completion handler that gets the result in.
   // You have to provide this function here and do something with the result
   //
   // The completion handler will only be run when the foo function calls it
   // (which is after the computation as you can see in the method above.
   // 
   // I am also telling the completion handler here that self should not be
   // held on to as the view controller might already have gone away when the
   // long calculation finished. The `[weak self]` thingy makes that inside
   // your completion handler self is an optional and might be nil (and it 
   // doesn't hold a strong reference to self, but that's a whole other topic)
   B.foo(completion: { [weak self] result in
       // Do something with the result
       // Since we are called back on the main queue we can also do UI stuff safely
       self?.hideLoadingIndicator()
   })
}

我希望这有点帮助。

异步编程可能很难学,但是你可以在这个主题上找到大量的教程和例子。

答案 1 :(得分:1)

嘿Hamish你可以用两种简单的方式做到这一点,

第一个是使用为函数提供的延迟语句。 在函数超出范围后执行延迟语句块。 这里有一个简单的例子来描述它。

func print1000000() {

//start displaying the loading indicator

    defer {
// hide the loading indicator and move to the next ViewController
        let seVC = storyboard?.instantiateViewController(withIdentifier: "SecondVC") as! SecondVC
        self.navigationController?.pushViewController(seVC, animated: true)
    }

// here goes the task you want to execute such as downloading a file or the one i did here
    for index in 0...1000000 {
        print(index)
    }
}

上面的函数打印数字达到1000000,然后将控件推送到另一个ViewController

=============================================== ==========================

第二种做法是使用闭包,正如Thomas在回答中所描述的那样。