1

so I'd like to lay a Color at the top of a ZStack over another view. The reason I don't want to use overlay is the Color is going to have a tap gesture attached to it. A minimal reproducible example is below. Essentially, I want the Color.secondary to be confined to the same area as the HStack (without explicitly setting frame size. Here's the code:

struct ContentView: View {
    var body: some View {
        ZStack {
            HStack {
                Spacer()
                Button(action: {
                    print("tapped button")
                }, label: {
                    Text("button")
                })
            }.background(Color.red)
            Color.secondary
                .onTapGesture {
                    print("clicked color")
                }
        }
    }
}

So I'd like the view to just be a white screen, with an HStack that looks slightly darker red.

Below is a picture of my UI. The view is greyed out during onboarding, and the user will essentially just tap the grey area to go to the next step in the onboarding. If I attach a tap gesture to the Color view and then just hide the color view according to state changes, this isn't a problem. After the onboarding is completed, the greyed area won't be there and the buttons underneath need to be interactable. When using overlays, AFTER onboarding, I don't want tapping anywhere on the view to change the app state.

https://i.stack.imgur.com/lphHg.png

nickcoding2
  • 142
  • 1
  • 8
  • 34
  • 1
    Overlay should be fine even if you have gestures. – aheze Dec 31 '21 at 00:05
  • @aheze In my more complex code, I have buttons and stuff that will be interfered with if it's not done this way. Also, don't want to mess around with button priorities. Any ideas? – nickcoding2 Dec 31 '21 at 00:07
  • 1
    Hmm interesting. But if you want the `Color.secondary` to take up the same space as the `HStack`, `overlay` is the best way. ZStack takes up the space of the biggest view (the `Color.secondary`) no matter that level they are at. Try `overlay`, then changing the order of your gesture modifiers. – aheze Dec 31 '21 at 00:22
  • @aheze The views inside of the stack are buttons though, so can't really play around with TapGesture priority. I'll update the code above to be a bit more reflective. – nickcoding2 Dec 31 '21 at 00:35
  • It would help if you could make a diagram or something about what behavior you want. Anyway `ZStack` would be almost impossible here (unless you're ok with messing with GeometryReader and overcomplicating everything...). Your question is more about gestures – aheze Dec 31 '21 at 00:55
  • I am a little lost in what you are trying to accomplish. By putting the `.onTapGesture` on the `Color.secondary` without a `simultaneousGesture()` you have already compromised the ability to use the button below. Can you show a mock-up of the UI and how it is to be used? – Yrb Dec 31 '21 at 00:57
  • @aheze The question's about how to fit the Color to the other view. If the only way to do this is with overlay, then gestures come into play. I updated with a pic of my UI and a bit of a better description of what the desired functionality is. – nickcoding2 Dec 31 '21 at 01:06
  • @Yrb I added a picture of the UI and a description. Not sure the description was comprehensible as it's pretty late at night where I am, but I gave it a shot – nickcoding2 Dec 31 '21 at 01:06

1 Answers1

1

Given your further description, I believe you have the onTapGesture(perform:) in the wrong place, outside of the overlay rather than inside.

In the code below, the onTapGesture(perform:) can only be tapped on the gray overlay when it is showing. However when it is attached after the overlay, it can be tapped on when not tapping on something like the button.

Working code:

struct ContentView: View {
    @AppStorage("is_onboarding") var isOnboarding = true

    var body: some View {
        HStack {
            Spacer()

            Button {
                print("tapped button")
            } label: {
                Text("button")
            }
        }
        .background(Color.red)
        .overlay {
            if isOnboarding {
                Color.secondary
                    .onTapGesture {
                        print("clicked color")
                        isOnboarding = false
                    }
            }
        }
    }
}

If on iOS 14+ not iOS 15+, use the following overlay instead:

.overlay(
    isOnboarding ?
        Color.secondary
            .onTapGesture {
                print("clicked color")
                isOnboarding = false
            }
    : nil
)
George
  • 25,988
  • 10
  • 79
  • 133