skip to Main Content

I am trying to show Activity indicator view on API call in my swiftUI application. I have created the Activity Indicator view and it’s working fine but I want to disable the user interaction while it is being displayed. To achieve this I have also tried allowsHitTesting(false) modifier but of no use 🙁 When I am clicking on the button is clickable.

enter image description here

ContentView

import SwiftUI

struct ContentView: View {
    var body: some View {
        return NavigationView {
            ZStack {
                VStack {
                    Button {
                        //
                        print("Button tapped")
                    } label: {
                        Text("Tap me")
                    }
                    .frame(width: 200, height: 60)
                    .background(.red)
                    
                    Spacer()

                }
                
                Loading()
                    .edgesIgnoringSafeArea(.all)
                    .allowsHitTesting(false)
            }
            
            }
         
        
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

IndicatorView

import SwiftUI

struct Loading: View {
    var body: some View {
        ZStack {
            BlurView()
            VStack {
                Indicator()
            }
        }.frame(maxWidth: .infinity, maxHeight: .infinity)
            .allowsHitTesting(false)
        
    }
}

//struct ActivityIndicatorView_Previews: PreviewProvider {
//    static var previews: some View {
//        ActivityIndicatorView(show: .constant(true))
//    }
//}

struct BlurView: UIViewRepresentable {
    func makeUIView(context: UIViewRepresentableContext<BlurView>) -> UIVisualEffectView {
        let effect = UIBlurEffect(style: .systemMaterial)
        let view = UIVisualEffectView(effect: effect)
        return view
    }
    
    func updateUIView(_ uiView: UIVisualEffectView, context: UIViewRepresentableContext<BlurView>) {
        //
    }
}


struct Indicator: UIViewRepresentable {
    func makeUIView(context: UIViewRepresentableContext<Indicator>)-> UIActivityIndicatorView {
        let ind = UIActivityIndicatorView(style: .medium)
        ind.startAnimating()
        return ind
    }
    
    func updateUIView(_ uiView: UIActivityIndicatorView, context: Context) {
        //
    }
}

2

Answers


  1. You dont need to use .allowsHitTesting(false) modifier.
    Just mark your blur view is not user-interactable. I tested on simulator.

    func makeUIView(context: UIViewRepresentableContext<BlurView>) -> UIVisualEffectView {
        let effect = UIBlurEffect(style: .systemMaterial)
        let view = UIVisualEffectView(effect: effect)
        view.isUserInteractionEnabled = false
        return view
    }
    
    Login or Signup to reply.
  2. If you want to disable the Button in your ContentView, you will have to apply the .allowsHitTesting(_:) modifier on the button, instead of Loading() (or BlurView). It is your button that is checking for taps, after all, not the progress indicator.

    struct ContentView: View {
        var body: some View {
            // ...
            
            Button {
                print("Button tapped")
            } label: {
                Text("Tap me")
            }
            .frame(width: 200, height: 60)
            .background(.red)
            .allowsHitTesting(false) // <-- Here
            
            // ...
        }
    }
    

    Presumably, you want the progress indicator to appear (and the button disabled) only when the API is actually making a call. The ‘loading’ state of the API would be communicated with some State variable. I would also suggest using .disabled(_:) instead of .allowsHitTesting(_:), although admittedly they are functionally the same in this scenario.

    struct ContentView: View {
        @State private var apiIsLoading = false
    
        var body: some View {
            // ...
            VStack {
                
                Button {
                    print("Button tapped")
                } label: {
                    Text("Tap me")
                }
                .frame(width: 200, height: 60)
                .background(.red)
                .disabled(apiIsLoading) // Button will be disabled when api is in a loading state
                Spacer()
            }
    
            if apiIsLoading {
                Loading()
                    .edgesIgnoringSafeArea(.all)
            }
            
            // ...
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search