-
-
Save edc0der/e4bed05b4c6653ffcd36c0609f27c7c6 to your computer and use it in GitHub Desktop.
import UIKit | |
fileprivate let overlayViewTag: Int = 999 | |
fileprivate let activityIndicatorViewTag: Int = 1000 | |
// Public interface | |
extension UIView { | |
func displayAnimatedActivityIndicatorView() { | |
setActivityIndicatorView() | |
} | |
func hideAnimatedActivityIndicatorView() { | |
removeActivityIndicatorView() | |
} | |
} | |
extension UIViewController { | |
private var overlayContainerView: UIView { | |
if let navigationView: UIView = navigationController?.view { | |
return navigationView | |
} | |
return view | |
} | |
func displayAnimatedActivityIndicatorView() { | |
overlayContainerView.displayAnimatedActivityIndicatorView() | |
} | |
func hideAnimatedActivityIndicatorView() { | |
overlayContainerView.hideAnimatedActivityIndicatorView() | |
} | |
} | |
// Private interface | |
extension UIView { | |
private var activityIndicatorView: UIActivityIndicatorView { | |
let view: UIActivityIndicatorView = UIActivityIndicatorView(style: .large) | |
view.translatesAutoresizingMaskIntoConstraints = false | |
view.tag = activityIndicatorViewTag | |
return view | |
} | |
private var overlayView: UIView { | |
let view: UIView = UIView() | |
view.translatesAutoresizingMaskIntoConstraints = false | |
view.backgroundColor = .black | |
view.alpha = 0.5 | |
view.tag = overlayViewTag | |
return view | |
} | |
private func setActivityIndicatorView() { | |
guard !isDisplayingActivityIndicatorOverlay() else { return } | |
let overlayView: UIView = self.overlayView | |
let activityIndicatorView: UIActivityIndicatorView = self.activityIndicatorView | |
//add subviews | |
overlayView.addSubview(activityIndicatorView) | |
addSubview(overlayView) | |
//add overlay constraints | |
overlayView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true | |
overlayView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true | |
//add indicator constraints | |
activityIndicatorView.centerXAnchor.constraint(equalTo: overlayView.centerXAnchor).isActive = true | |
activityIndicatorView.centerYAnchor.constraint(equalTo: overlayView.centerYAnchor).isActive = true | |
//animate indicator | |
activityIndicatorView.startAnimating() | |
} | |
private func removeActivityIndicatorView() { | |
guard let overlayView: UIView = getOverlayView(), let activityIndicator: UIActivityIndicatorView = getActivityIndicatorView() else { | |
return | |
} | |
UIView.animate(withDuration: 0.2, animations: { | |
overlayView.alpha = 0.0 | |
activityIndicator.stopAnimating() | |
}) { _ in | |
activityIndicator.removeFromSuperview() | |
overlayView.removeFromSuperview() | |
} | |
} | |
private func isDisplayingActivityIndicatorOverlay() -> Bool { | |
getActivityIndicatorView() != nil && getOverlayView() != nil | |
} | |
private func getActivityIndicatorView() -> UIActivityIndicatorView? { | |
viewWithTag(activityIndicatorViewTag) as? UIActivityIndicatorView | |
} | |
private func getOverlayView() -> UIView? { | |
viewWithTag(overlayViewTag) | |
} | |
} |
Thanks, dude!
you're welcome. If you have any suggestions, be sure to let me know.
thank you. very cool
Update to this in case it serves anyone:
import UIKit
import With
fileprivate let overlayViewTag = 999
fileprivate let activityIndicatorViewTag = 1000
extension UIView {
private var activityIndicatorView: UIActivityIndicatorView {
let view = UIActivityIndicatorView(style: .large)
view.translatesAutoresizingMaskIntoConstraints = false
view.tag = activityIndicatorViewTag
return view
}
//
private var overlayView: UIView {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.black
view.alpha = 0.5
view.tag = overlayViewTag
return view
}
//
public func displayActivityIndicator(shouldDisplay: Bool) {
shouldDisplay ? setActivityIndicatorView() : removeActivityIndicatorView()
}
//
private func setActivityIndicatorView() {
guard !isDisplayingActivityIndicatorOverlay() else { return }
let overlayView = self.overlayView
let activityIndicatorView = self.activityIndicatorView
//add subviews
overlayView.addSubview(activityIndicatorView)
addSubview(overlayView)
//add overlay constraints
overlayView.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
overlayView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
//add indicator constraints
activityIndicatorView.centerXAnchor.constraint(equalTo: overlayView.centerXAnchor).isActive = true
activityIndicatorView.centerYAnchor.constraint(equalTo: overlayView.centerYAnchor).isActive = true
//animate indicator
activityIndicatorView.startAnimating()
}
//
private func removeActivityIndicatorView() {
let activityIndicator = getActivityIndicatorView()
guard let overlayView = getOverlayView() else { return }
UIView.animate(withDuration: 0.2, animations: {
overlayView.alpha = 0.0
activityIndicator?.stopAnimating()
}) { (finished) in
activityIndicator?.removeFromSuperview()
overlayView.removeFromSuperview()
}
}
//
private func isDisplayingActivityIndicatorOverlay() -> Bool {
getActivityIndicatorView() != nil
&& getOverlayView() != nil
}
//
private func getActivityIndicatorView() -> UIActivityIndicatorView? {
viewWithTag(activityIndicatorViewTag) as? UIActivityIndicatorView
}
//
private func getOverlayView() -> UIView? {
viewWithTag(overlayViewTag)
}
}
Thank you, @acamill for your response and suggestion, I'll give it a try.
Looking at it, it seems you took the responsibility out of the UIViewController, would it mean that now in order to use it from a ViewController you'd have to write
view.displayActivityIndicator(shouldDisplay: true)
?
Would that also mean the overlay would cover any view that called it? i.e, from a cell from a TableView would it only overlay the cell?
As I said, I'll try it and see how it works. And thank you very much
@edc0der yes! I ended up not not using it even before displaying anything in the end, but it made more sense to me to have it at the view level as it’s still UI elements?
And yes I think it will appears on any view and just in that view
Updated the snippet with @acamill's suggested code. Thank you!
Changes:
- Added type annotation
- Separated the display method into display and hide, in order to have single responsibility methods
- Added UIViewController extension to use the overlay to cover the fullscreen without having to write
navigationController?.view.displayAnimatedActivityIndicatorView()
How is this applied to a normal swiftUI page. If I want the page to display the loading icon how do I do that?
Import SwiftUI
struct Welcome: View {
var body: some View {
<< DISPLAY LOADING HERE >>
Text("Hello World")
}
}
struct Dashboard_Previews: PreviewProvider {
static var previews: some View {
Welcome()
}
}
Thanks for sharing!
Thanks for this code snippet. Very handy!