活动指示器未出现

8

我有一些运行时间约为0.2秒的繁重代码。

我像这样设置了活动指示器;然而,它没有出现,而是整个屏幕在0.2秒左右冻结,直到代码运行完毕。

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

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

    self.activityIndicator.stopAnimating()
}

这是使用活动指示器的正确方法吗?
当我注释掉

// self.activityIndicator.stopAnimating()

活动指示器出现并停留在那里——代码设置正确。

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

正如我所说,屏幕在重负载代码完成之前会冻结,并未显示活动指示器。


2
你的“繁重循环代码”正在后台线程上运行吗?这个繁重的循环代码会阻止 UI 显示活动指示器。如果你在后台线程上调用“heavyWork”,那么 UI 不应该更新,因为它不在主线程上。 - sbarow
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - holex
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更新转移到主线程上。


8
如果您希望应用程序在执行一些繁重的任务时保持响应性,则需要在后台线程上执行它。
大致情况如下:您的应用程序的主线程在运行循环中执行。在每个循环迭代的开始,iOS都会检查是否有任何事件(例如用户交互,由于动画而更改的视图,定时器被触发等),然后排队执行一堆方法。然后,iOS执行每个方法,然后一切都完成后,更新显示。然后开始下一个运行循环迭代。更新显示是代价高昂的,因此iOS不能在执行每行代码后立即进行更新。
因此,在您的代码中,当您告诉activityIndicator开始运行时,它告诉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()方法返回重型工作的结果。这是因为该函数将异步代码安排在不同线程上运行,然后立即返回,以便可以继续当前运行循环迭代。

SWIFT 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()
        }
    }
}

非常感谢这个答案!我一整天都在苦苦思索DispatchQueue和活动指示器。 - Zhou Haibo

7

SWIFT 5:

func heavyWork() {
    activityIndicator.startAnimating()
    
    DispatchQueue.global(qos: .background).async {
        
        // ...
        // heavy loop codes here
        // ...
        
        DispatchQueue.main.async {
            self.activityIndicator.stopAnimating()
        }
    }
}

1
这是因为您在主线程上运行了重型代码。这样,系统永远没有机会在重型例程结束之前提交图形事务。您的启动和停止方法同时被提交。
为了避免这种情况,您应该在另一个dispatch_queue上运行重型例程,但要注意大多数UIKit对象不是线程安全的,应该再次分派到主队列上。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接