假设我有一个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()
})
}
答案 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在回答中所描述的那样。