ios – 启动和停止活动指示器swift3

我正在学习 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()

然后会发生什么:

您排队调用以在下次代码返回时启动活动指示器. (所以它还没发生.)

您通过一些耗时的任务来阻止主线程.在此过程中,您的活动指示器尚未开始旋转.

最后,您的耗时任务完成.现在您的代码调用activityIndi​​cator.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来停止活动指示器.

相关文章

UITabBarController 是 iOS 中用于管理和显示选项卡界面的一...
UITableView的重用机制避免了频繁创建和销毁单元格的开销,使...
Objective-C中,类的实例变量(instance variables)和属性(...
从内存管理的角度来看,block可以作为方法的传入参数是因为b...
WKWebView 是 iOS 开发中用于显示网页内容的组件,它是在 iO...
OC中常用的多线程编程技术: 1. NSThread NSThread是Objecti...