swift: индикатор активности после контроллера предупреждений

Если я создаю индикатор активности программно в обработчике alertAction - он появляется только после завершения кода и если нет activityIndicator.stopAnimating(). Почему? Я хочу, чтобы индикатор активности вращался во время обработки трудоемкой функции.

let alertController = UIAlertController(title: "Add", message: "", preferredStyle: .alert)

alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: { _ in

let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
activityIndicator.color = .black
activityIndicator.center = self.view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, .flexibleBottomMargin]
activityIndicator.startAnimating()
self.view.addSubview(activityIndicator)

timeConsumingFunc()

activityIndicator.stopAnimating()
}))

present(alertController, animated: true)

person Anton    schedule 24.02.2018    source источник


Ответы (3)


Пользовательский интерфейс не будет реагировать сразу после того, как вы это сделаете:

self.view.addSubview(activityIndicator)

Пользовательский интерфейс не будет обновляться до следующего кадра. За время между этим кадром и следующим кадром вы уже начали выполнять трудоемкую функцию и запускаете ее синхронно, поэтому ваш UI зависает. Следующий кадр рисуется, когда завершается его выполнение.

Вам нужно выполнить работу асинхронно:

DispatchQueue.main.async {
    [weak self] in 
    self?.timeConsumingFunc()
    self?.activityIndicator.stopAnimating()
}

или вообще в другой очереди отправки.

person Sweeper    schedule 24.02.2018

Изменение пользовательского интерфейса происходит только в основном потоке, когда он представляет собой блок. Согласно вашему сценарию, вы должны асинхронно запустить этот фрагмент кода в основном потоке.

DispatchQueue.main.async { [weak self] in //[weak self] is used for precaution from memory leak
    self?.timeConsumingFunc()
    self?.activityIndicator.stopAnimating()
}
person Pratyush Pratik    schedule 24.02.2018

Вы должны запустить свою трудоемкую задачу в фоновой очереди и переключиться обратно в основную очередь, чтобы обновить пользовательский интерфейс:

DispatchQueue.global().async {
    timeConsumingFunc()
    DispatchQueue.main.async {
        activityIndicator.stopAnimating()
    }
}

Вы также можете упростить и улучшить индикатор активности, используя NVActivityIndicatorView:

let activityData = ActivityData()
NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData)
NVActivityIndicatorPresenter.sharedInstance.setMessage("Doing stuff...")
DispatchQueue.global().async {
    timeConsumingFunc()
    DispatchQueue.main.async {
        NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
    }
}
person sundance    schedule 24.02.2018