112

Let's say I have a SwiftUI view hierarchy that looks like this:

ZStack() {
    ScrollView {
        ...
    }
    Text("Hello.")
}

The Text view blocks touch events from reaching the underlying ScrollView.

With UIKit, I'd use something like .isUserInteractionEnabled to control this, but I can't find any way to do this with SwiftUI.

I've tried adding a Gesture with a GestureMask of .none on the text view, but that doesn't seem to work.

I hope I'm missing something obvious here, because I need to put some status information on top of the scroll view.

8
  • 1
    Between the "rawness" of SwiftUI and the outlier need you described, I'd be surprised if you are missing something obvious - it probably doesn't (yet) exist. Commented Sep 11, 2019 at 20:40
  • You probably want to put the Text in a frame to make it smaller and contentShape may help too. Commented Sep 12, 2019 at 8:36
  • @chockenberry It's exactly one year later. Did you find a working solution for the Problem and would you might share it? Commented Sep 11, 2020 at 12:54
  • Interesting how SwiftUI seemed to automatically disable user interaction if opacity was ≤ 0.5 if I didn't set allowsHitTesting. Commented Sep 15, 2020 at 1:34
  • 2
    Found an answer: stackoverflow.com/a/61225965/11912101 .allowsHitTesting(false)applying to the UIViewControllerWrapper() did the trick for me. Commented Oct 21, 2020 at 12:27

10 Answers 10

133

What about using the .allowsHitTesting()?

https://developer.apple.com/documentation/swiftui/image/3269586-allowshittesting

From my understanding it should pass the gesture to the view behind if it's disabled.

ZStack() {
    ScrollView {
        ...
    }
    Text("Hello.").allowsHitTesting(false)
}
Sign up to request clarification or add additional context in comments.

5 Comments

It works for me. I have images over background view and want to detect gestures on background. So adding allowsHitTesting(false) on images disables them from interfering with gesture recognitiona and all touches are handled by background view.
As a sidenote, this was not working for me initially since I was adding a drop modifier, but when I moved this to be the last modifier it started working.
This is currently not working for me, I have a Circle in front of a Map UIViewRepresentable and I can't scroll the map if I'm dragging on the Circle itself
This does not work with iOS14; the Text will block taps where the Text appears in the ZStack.
omg, Macromedia Flash is back
51

There is a modifier on View to disable or enable user interaction:

disabled(_ disabled: Bool)

4 Comments

I tried putting .disabled(true) on both the Text and ZStack and that didn't help.
Apple doc say: "The higher views in a view hierarchy can override the value you set on this view. In the following example, the button isn't interactive because the outer disabled(_:) modifier overrides the inner one: HStack { Button(Text("Press")) {} .disabled(false) } .disabled(true)
Have you tried setting zIndex(1) on one of the views to bring it forward?
See my own answer above: it appears that the problem isn't the stack or views within it, but rather a problem with .border() doing something unexpected.
34

You want to make sure you fill your view with some color, except Clear color (you can always change opacity). I've also added blur to hide elements. Here's what worked for me:

SwiftUI:

ZStack{
    SomeView().blur(radius: 12)
    Rectangle()
        .fill(Color.white.opacity(0))
        .allowsHitTesting(false)
}

Another way to disable user interactions like scroll or button taps, but attach an action to user taps (for example a message to users that this feature is coming or behind a paywall):

SwiftUI:

VStack{
    SomeView().blur(radius: 12)
}
.contentShape(Rectangle())
.onTapGesture {
    print("No access!")
}

Comments

27

Put your view to be "tap disabled" into a Group then apply the modifier .allowsHitTesting(false) to disable interaction with any view inside the group.

Group {
  Button("Hello")
}
.allowsHitTesting(false)

To enable interaction switch the modifier to true.

1 Comment

This is the most useful answer
15

SwiftUI 2.0

 ZStack {

    // Your Stack to Disable User Interaction

}.disabled(showLoadingIndicator)

3 Comments

You really can change it just to .disabled(showLoadingIndicator)
.disabled(showLoadingIndicator) here showLoadingIndicator is Boolean
This works great with ScrollViews as well as it cancels their partial pagination if you say trigger a navigation gesture on a slight angle, while applying it on a ScrollView that paginates like a pager 🎉
4

This is most likely a bug in SwiftUI, but the workaround for this problem was to remove a .border() that I had put on the Text view for bounds debugging.

Comments

1

Certain elements inside of SwiftUI cannot be disabled through .disabled or .allowHitTesting. A major HACK is to wrap your element in a TabView and apply a paging style to it. Again this is a major hack but it's an interesting solution I'm using currently.

Comments

1
ZStack {


}
.allowsHitTesting(false)

it will disabled the user interaction

Comments

0

I'm doing something similar on a watch app, .overlaying a text field over a Slider. I couldn't get either .disable or .allowHitTesting to work when applied to the Text field.

To solve this, I found that placing the Text field in a Group{} (all by itself) and then applying the .allowHitTesting(false) worked perfectly.

Comments

0

You can use the below modifier

EX:

view.loader(isLoading)

Modifer ⬇️

//
//  LoaderModifier.swift
//  
//
//  Created by Metin Atalay 
//

import SwiftUI
import TRTUI

public struct LoaderModifier: ViewModifier {
    public var isLoading: Bool
    
    public func body(content: Content) -> some View {
        content.disabled(isLoading)
            .overlay {
                if isLoading {
                    ZStack(alignment: .center) {
                       ProgressView()
                    }
                    .frame(width: 200, height: 150)
                    .zIndex(100)
                }
            }
    }
}

// MARK: VIEW EXTENSION
public extension View {
    func loader(_ loading: Bool) -> some View {
        self.modifier(LoaderModifier(isLoading: loading))
    }
}

1 Comment

So basically only relevant modifier here is .disabled() and everything else is offtopic.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.