Ситуация, в которой вы оказались, кажется достаточной причиной, чтобы пересмотреть то, что вы сделали, чтобы достичь этого. Я использую, я думаю, похожую ситуацию, когда я загружаю все основные объекты данных в отдельном потоке, поэтому используются дополнения, такие как
MyEntity.fetchAll { items,
self.entities = items
self.tableView.reloadData()
}
В этом случае довольно легко сделать что-то вроде:
var entities: [Any]? {
didSet {
self.removeActivityIndicator()
}
}
Вы можете поместить всю логику в некоторый базовый класс для вашего контроллера представления, чтобы вы могли легко использовать его повторно.
Иногда, хотя лучше делать это статически. Вы можете добавить новое окно над всем, что имеет индикатор активности. В основном, как делать пользовательские представления предупреждений. Лучшая система подсчета должна работать лучше:
class ActivityManager {
private static var retainCount: Int = 0 {
didSet {
if(oldValue > 0 && newValue == 0) removeActivityWindow()
else if(oldValue == 0 && newValue > 0) showActivityWindow()
}
}
static func beginActivity() { retainCount += 1 }
static func endActivity() { retainCount -= 1 }
}
В этом случае вы можете использовать инструмент в любом месте вашего кода. Правило состоит в том, что у каждого «начала» должен быть «конец». Так, например:
func resolveData() {
ActivityManager.beginActivity()
doMagic {
ActivityManager.endActivity()
}
}
Есть действительно много способов сделать это, и, вероятно, не существует «лучшего решения», поскольку это зависит только от вашего случая.
Пример использования нового окна для отображения диалога:
В соответствии с просьбой в комментариях я добавляю пример того, как показать диалоговое окно в новом окне. Я использую новую раскадровку "Диалог", которая содержит контроллер вида AlertViewController
. Это также может быть контроллер с некоторым индикатором активности, но более важной частью является то, как генерируется окно, как отображается контроллер и как его закрывают.
class AlertViewController: UIViewController {
@IBOutlet private var blurView: UIVisualEffectView?
@IBOutlet private var dialogPanel: UIView?
@IBOutlet private var titleLabel: UILabel? // Is in vertical stack view
@IBOutlet private var messageLabel: UILabel? // Is in vertical stack view
@IBOutlet private var okButton: UIButton? // Is in horizontal stack view
@IBOutlet private var cancelButton: UIButton? // Is in horizontal stack view
var titleText: String?
var messageText: String?
var confirmButtonText: String?
var cancelButtonText: String?
override func viewDidLoad() {
super.viewDidLoad()
setHiddenState(isHidden: true, animated: false) // Initialize as not visible
titleLabel?.text = titleText
titleLabel?.isHidden = !(titleText?.isEmpty == false)
messageLabel?.text = messageText
messageLabel?.isHidden = !(messageText?.isEmpty == false)
okButton?.setTitle(confirmButtonText, for: .normal)
okButton?.isHidden = !(confirmButtonText?.isEmpty == false)
cancelButton?.setTitle(cancelButtonText, for: .normal)
cancelButton?.isHidden = !(cancelButtonText?.isEmpty == false)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setHiddenState(isHidden: false, animated: true)
}
private func setHiddenState(isHidden: Bool, animated: Bool, completion: (() -> Void)? = nil) {
UIView.animate(withDuration: animated ? 0.3 : 0.0, animations: {
self.blurView?.effect = isHidden ? UIVisualEffect() : UIBlurEffect(style: .light)
self.dialogPanel?.alpha = isHidden ? 0.0 : 1.0
}) { _ in
completion?()
}
}
@IBAction private func okPressed() {
AlertViewController.dismissAlert()
}
@IBAction private func cancelPressed() {
AlertViewController.dismissAlert()
}
}
// MARK: - Window
extension AlertViewController {
private static var currentAlert: (window: UIWindow, controller: AlertViewController)?
static func showMessage(_ message: String) {
guard currentAlert == nil else {
print("An alert view is already shown. Dismiss this one to show another.")
return
}
let controller = UIStoryboard(name: "Dialog", bundle: nil).instantiateViewController(withIdentifier: "AlertViewController") as! AlertViewController
controller.confirmButtonText = "OK"
controller.messageText = message
let window = UIWindow(frame: UIApplication.shared.windows[0].frame)
window.windowLevel = .alert
window.rootViewController = controller
window.makeKeyAndVisible()
self.currentAlert = (window, controller)
}
static func dismissAlert() {
if let currentAlert = self.currentAlert {
currentAlert.controller.setHiddenState(isHidden: true, animated: true) {
self.currentAlert?.window.isHidden = true
self.currentAlert = nil
}
}
}
}
Я добавил весь класс на всякий случай, но важная часть показывает новое окно:
let window = UIWindow(frame: UIApplication.shared.windows[0].frame) // Create a window
window.windowLevel = .alert // Define which level it should be in
window.rootViewController = controller // Give it a root view controller
window.makeKeyAndVisible() // Show the window
и удаление окна:
window.isHidden = true
Достаточно просто спрятать окно. Предполагая, что у вас нет сильной ссылки на него, он будет удален из стека приложения. Для подтверждения этого убедитесь, что UIApplication.shared.windows.count
имеет соответствующее значение, которое в большинстве случаев должно быть 2
, когда отображается предупреждение, и 1
в противном случае.
Мое тестовое использование приведенного выше кода было просто:
AlertViewController.showMessage("A test message. This is testing of alert view in a separate window.")