Home > front end >  How to disable user interaction for view in SwiftUI?
How to disable user interaction for view in SwiftUI?

Time:11-10

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) {
        //
    }
}

CodePudding user response:

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
}

CodePudding user response:

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)
        }
        
        // ...
    }
}
  • Related