8

我有一些运行大约 0.2 秒的繁重代码。

我这样设置活动指示器;但是,它没有显示出来,而是整个屏幕冻结了大约 0.2 秒,直到代码完成。

func heavyWork() {
    self.actvityIndicator.startAnimating()

    ...
    // heavy loop codes here
    ...

    self.activityIndicator.stopAnimating()
}

这是使用活动指示器的正确方法吗?

当我注释掉

// self.activityIndicator.stopAnimating()

活动指示器显示并停留在那里 - 代码设置正确。

但是 UI 似乎没有在正确的时间更新。

正如我所说,屏幕只是冻结而不显示活动指示器,直到完成繁重的代码。

4

4 回答 4

15

也许您想继续使用这种模式:

func heavyWork() {
    self.actvityIndicator.startAnimating()

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in

        // ...
        // heavy loop codes here
        // ...

        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            self.activityIndicator.stopAnimating()
        })
    });
}

因为繁重的工作应该发生在后台线程中,之后您需要在线程上更新 UI。


注意:显然假设您 func heavyWork() 在主线程上调用;如果没有,您可能还需要将初始 UI 更新转移到主线程。

于 2015-07-07T13:30:22.697 回答
8

如果您希望应用程序在执行一些繁重的任务时响应,您将需要在后台线程上执行它。

这大致是这里发生的事情:您的应用程序的主线程在运行循环中执行。在每个循环迭代开始时,iOS 会检查任何事件(例如用户交互、由于动画而改变的视图、触发计时器等),然后将一堆方法排队等待执行。iOS 然后去执行这些方法中的每一个,然后,一旦一切都完成,它会更新显示。然后下一个运行循环迭代开始。更新显示的成本很高,因此 iOS 无法在执行完每一行代码后进行。

因此,使用您的代码,当您告诉 activityIndi​​cator 开始动画时,它会告诉 iOS 在每次运行循环迭代结束时,活动指示器图像需要更新为动画序列中的下一个图像。然后,在 iOS 到达当前运行循环迭代的末尾之前,您将调用 stopAnimating 来告诉 iOS 它不再需要更新图像。所以基本上你告诉它在它开始之前停止。

您可以使用 Grand Central Dispatch 在不同的线程上轻松运行代码。但是需要注意的是,对 UI 的任何更新都必须在主线程上完成。

func heavyWork() {
    self.activityIndicator.startAnimating()

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {

        // Do heavy work here

        dispatch_async(dispatch_get_main_queue()) {
            // UI updates must be on main thread
            self.activityIndicator.stopAnimating()
        }
    }
}

另请注意,在异步编程时,例如在上面的示例中,您不能从调用它的方法中的异步部分返回值。例如,在上面的示例中,您不能从 heavyWork() 方法返回繁重工作的结果。这是因为该函数将异步代码安排在不同的线程上运行,然后立即返回,这样它就可以继续当前的运行循环迭代。

斯威夫特 4

func heavyWork() {
    activityIndicator.startAnimating()

    DispatchQueue.global(qos: .default).async {

        // Do heavy work here

       DispatchQueue.main.async { [weak self] in
            // UI updates must be on main thread
            self?.activityIndicator.stopAnimating()
        }
    }
}
于 2015-07-07T14:04:25.040 回答
5

斯威夫特 4:

func heavyWork() {
    self.actvityIndicator.startAnimating()

    DispatchQueue.global(qos: .background).async {

        // ...
        // heavy loop codes here
        // ...

        DispatchQueue.main.async {
            self.actvityIndicator.stopAnimating()
        }
    }
}
于 2018-01-31T19:45:31.683 回答
1

发生这种情况是因为您在主线程上运行繁重的代码。这样,系统在繁重的例程结束之前永远没有机会提交图形事务。您的 start 和 stop 方法同时提交。
为避免这种情况,您应该在 another 上运行重型方法dispatch_queue,但请注意大多数 UIKit 对象不是线程安全的,应在主队列上再次分派。

于 2015-07-07T13:28:48.983 回答