我正在学习
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来停止活动指示器.