I'm encountering an issue when trying to present a loader over my views. The loader fails to cover the navigation, allowing users to interact with the navigation elements, such as tapping the back button, which creates an awkward user experience.
It's important to note that while all my views are built using SwiftUI, I'm using UIKit-based navigation. Our application needs to support older iOS versions, and our team has decided to use UIKit navigation for its flexibility and maturity compared to SwiftUI navigation.
How can I ensure the loader covers the entire view, including the navigation bar, in this mixed SwiftUI and UIKit environment? Any advice or solutions would be greatly appreciated.
In the scenario above:
ActivityIndicatorView(Loader)
public struct ActivityIndicatorView<Content>: View where Content: View {
public var isActive: Bool
public var content: () -> Content
public init(isActive: Bool, content: @escaping () -> Content) {
self.isActive = isActive
self.content = content
}
public var body: some View {
self.content()
.disabled(self.isActive)
.background(self.isActive ? Color.black.opacity(0.25) : .clear)
.blur(radius: self.isActive ? 3 : 0)
.overlay {
if isActive {
Image("Loader/Container", bundle: .shamrockCore)
.overlay {
ActivityIndicator(isAnimating: self.isActive, style: .large)
}
.zIndex(1)
}
}
}
}
PasswordRecoveryView
struct PasswordRecoveryView: View {
private(set) var coordinator: LoginCordinator
@StateObject private var viewModel = PasswordRecoveryViewModel()
private var confimrationEmailButton: some View {
// Submit Button View
}
private var emailField: some View {
// Email View
}
var body: some View {
ActivityIndicatorView(isActive: true) {
ZStack {
Theme.PrimaryBackground
.ignoresSafeArea()
VStack {
VStack(spacing: 40) {
Text(L.localize(.enter_email_text))
.multilineTextAlignment(.center)
.foregroundColor(Theme.TintPrimaryLight)
.font(.metropolis(.regular, size: 20))
emailField
}
.padding(.horizontal, 30)
.padding(.top, 15)
Spacer()
confimrationEmailButton
.padding(.bottom)
}
}
.navigationBarHidden(false)
.navigationTitle(L.localize(.forgot_password_screen_title))
.onReceive(viewModel.$didForgotPasswordEmailGenerated) { isEmailGenerated in
if isEmailGenerated {
coordinator.goToPasswordRecoveryConfirmationView()
}
}
}
}
}
Loader is covering only content of current pushed view's body. you can make separate loader class for this purpose that's how it can be used entirely to anywhere:
Loader Class
class Loader {
static var spinner: UIActivityIndicatorView?
static func show() {
guard let window = UIApplication.shared.windows.first else { return }
let spinnerView = UIView(frame: window.bounds)
spinnerView.backgroundColor = UIColor(white: 0, alpha: 0.5)
let activityIndicator = UIActivityIndicatorView(style: .large)
activityIndicator.center = spinnerView.center
activityIndicator.startAnimating()
spinnerView.addSubview(activityIndicator)
window.addSubview(spinnerView)
spinner = activityIndicator
}
static func hide() {
spinner?.superview?.removeFromSuperview()
spinner = nil
} }