SwiftKickMobile / SwiftMessages

A very flexible message bar for UIKit and SwiftUI.
MIT License
7.24k stars 742 forks source link

Knowing when an hide operation is completed #413

Open giofid opened 3 years ago

giofid commented 3 years ago

Hi @wtmoose, In my app, I'm using SwiftMessages to show an activity-indicator view during a time consuming operation. All it's ok. I found the indefinite(delay: TimeInterval, minimum: TimeInterval) enum case very useful. Now, when my time consuming operation is over (a network request for example), I have to push a new view controller. If I set the delay property to 1s and the minimum property to 1s, and my operation finishes in 1.5 seconds, I will still see the activity-indicator view for about 0.5 seconds after the push. In my opinion, to solve this, it would be useful to know when an hide operation is effectively completed to do the push only then. Something like:

open func hide(id: String, completion: (() -> Void)? = nil)

What do you think of this?

wtmoose commented 3 years ago

Have you looked at SwiftMessages.Config.eventListeners?

giofid commented 3 years ago

Sorry, I should have looked harder!!

However, I think SwiftMessages.Config.eventListeners is not suitable in my use case. eventListeners property has to be set when I show the activity-indicator view, but I will know what to do only after the time consuming operation is completed. For example, a network request could be finish successfully or with an error. In the former case I have to push a new view controller; in the latter one I have to present an alert.

wtmoose commented 3 years ago

Thinking about this...

giofid commented 3 years ago

Hi @wtmoose, Meanwhile, I solved with this workaround

final class HUD {

    static let shared = HUD()

    private let swiftMessages = SwiftMessages()

    private var didHideHandler: (() -> ())?

    private lazy var activityIndicatorView: ActivityIndicatorView = {
        let activityIndicatorView: ActivityIndicatorView = try! SwiftMessages.viewFromNib()
        activityIndicatorView.id = "ActivityIndicatorView"
        return activityIndicatorView
    }()

    private lazy var activityIndicatorConfig: SwiftMessages.Config = {
        var config = SwiftMessages.defaultConfig
        config.presentationContext = .window(windowLevel: UIWindow.Level.normal)
        config.presentationStyle = .center
        config.interactiveHide = false
        config.dimMode = .gray(interactive: false)
        config.eventListeners.append() { [weak self] event in
            if case .didHide = event {
                self?.didHideHandler?()
                self?.didHideHandler = nil
            }
        }
        return config
    }()

    func show(delay: TimeInterval = 0.5, minimum: TimeInterval = 2) {
        activityIndicatorConfig.duration = .indefinite(delay: delay, minimum: minimum)
        swiftMessages.show(config: activityIndicatorConfig, view: activityIndicatorView)
        swiftMessages.hide(id: messageView.id)
    }

    func hide(completion: (() -> Void)? = nil) {
        didHideHandler = completion
        swiftMessages.hideAll()
    }
}

I took advantage of the fact that closure behaves as a reference type.

However, as stated here, I don't know if it's a good idea to have a closure (eventListeners: [EventListener] closures' array in this case) in a struct (Config).