我正在学习
Swift,现在正在完成我的家庭任务.
我的一个按钮调用一个函数,我重新加载一个集合视图,这需要时间.我正在尝试实施一个活动指标来显示该过程.
问题是,如果我启动指示器,加载集合视图,然后在按钮操作中停止指示器,则指示器不会出现.
在这种情况下,实施指标的正确方法是什么?
这是代码片段:
我的一个按钮调用一个函数,我重新加载一个集合视图,这需要时间.我正在尝试实施一个活动指标来显示该过程.
问题是,如果我启动指示器,加载集合视图,然后在按钮操作中停止指示器,则指示器不会出现.
在这种情况下,实施指标的正确方法是什么?
这是代码片段:
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.whiteLarge) @IBOutlet weak var onCIFilterView: UICollectionView! @IBOutlet var cifilterMenu: UIView! @IBAction func onCIFiltersButton(_ sender: UIButton) { if (sender.isSelected) { hideCIFilters() sender.isSelected = false } else { activityIndicator.startAnimating() showCIFilters() //the menu with UIView collection activityIndicator.stopAnimating() sender.isSelected = true } } func showCIFilters () { onCIFilterView.reloadData() // reloading collection view view.addSubview (cifilterMenu) let bottomConstraint = cifilterMenu.bottomAnchor.constraint(equalTo: mainMenu.topAnchor) let leftConstraint = cifilterMenu.leftAnchor.constraint(equalTo: view.leftAnchor) let rightConstraint = cifilterMenu.rightAnchor.constraint(equalTo: view.rightAnchor) let heightConstraint = cifilterMenu.heightAnchor.constraint(equalToConstant: 80) NSLayoutConstraint.activate([bottomConstraint,leftConstraint,rightConstraint,heightConstraint]) view.layoutIfNeeded() } func hideCIFilters () { currentimage = myImage.image cifilterMenu.removeFromSuperview() } override func viewDidLoad() { super.viewDidLoad() //..... activityIndicator.center = CGPoint(x: view.bounds.size.width/2,y: view.bounds.size.height/2) activityIndicator.color = UIColor.lightGray view.addSubview(activityIndicator) }
解决方法
您面临的问题是,在您的代码返回并且应用程序为事件循环提供服务之前,UI更改(如启动活动指示符)不会发生.
您的代码是同步的,因此活动指示器永远不会被调用.
您需要启动活动指示器旋转,返回,然后在延迟后执行耗时的活动.您可以使用dispatch_after(或Swift 3中的dispatchQueue.main.asyncAfter).
以下是一个示例IBAction,它在点击按钮后运行活动指示器2秒:
@IBAction func handleButton(_ sender: UIButton) { activityIndicator.startAnimating() sender.isEnabled = false dispatchQueue.main.asyncAfter(deadline: .Now() + 0.01) { // your time-consuming code here sleep(2) //Do NOT use sleep on the main thread in real code!!! self.activityIndicator.stopAnimating() sender.isEnabled = true } }
编辑:
你的代码看起来像这样:
@IBAction func onCIFiltersButton(_ sender: UIButton) { if (sender.isSelected) { hideCIFilters() sender.isSelected = false } else { activityIndicator.startAnimating() dispatchQueue.main.asyncAfter(deadline: .Now() + 0.0) { showCIFilters() //the menu with UIView collection activityIndicator.stopAnimating() sender.isSelected = true } } }
编辑#2:
同样,在您的代码RETURNS和应用程序为事件循环提供服务之前,UI更改实际上并不会发生
如果您有这样的代码:
activityIndicator.startAnimating() sleep(2) //This is in place of your time-consuming synchronous code. activityIndicator.stopAnimating()
然后会发生什么:
您排队调用以在下次代码返回时启动活动指示器. (所以它还没发生.)
您通过一些耗时的任务来阻止主线程.在此过程中,您的活动指示器尚未开始旋转.
最后,您的耗时任务完成.现在您的代码调用activityIndicator.stopAnimating().最后,您的代码返回,您的应用程序服务事件循环,以及启动调用,然后立即停止调用活动指示器.结果,活动指标实际上并没有旋转.
这段代码的不同之处是:
//Queue up a call to start the activity indicator activityIndicator.startAnimating() //Queue up the code in the braces to be called after a delay. //This call returns immediately,before the code in the braces is run. dispatchQueue.main.asyncAfter(deadline: .Now() + 0.01) { showCIFilters() //the menu with UIView collection activityIndicator.stopAnimating() sender.isSelected = true }
由于关闭传递给dispatchQueue.main.asyncAfter的调用没有立即运行,该调用立即返回.
应用程序返回服务事件循环,活动指示器开始旋转.不久之后,Grand Central dispatch会调用您传递给它的代码块,它会开始执行耗时的操作(在主线程上),然后在耗时的任务完成后生成一个cal来停止活动指示器.