Button Touch Not Canceled in ScrollView on Modal in SwiftUI for iOS 18

When displaying a view with a Button inside a ScrollView using the sheet modifier, if you try to close the sheet by swiping and your finger is touching the Button, the touch is not canceled.

This issue occurs when building with Xcode 16 but does not occur when building with Xcode 15.

Here is screen cast.

https://drive.google.com/file/d/1GaOjggWxvjDY38My4JEl-URyik928iBT/view?usp=sharing

Code

struct ContentView: View {
    @State var isModalPresented: Bool = false
    var body: some View {
        ScrollView {
            Button {
                debugPrint("Hello")
                isModalPresented.toggle()
            } label: {
                Text("Hello")
                    .frame(height: 44)
            }
            Button {
                debugPrint("World")
            } label: {
                Text("World")
                    .frame(height: 44)
            }
            Text("Hoge")
                .frame(height: 44)
                .contentShape(Rectangle())
                .onTapGesture {
                    debugPrint("Hoge")
                }
        }
        .sheet(isPresented: $isModalPresented) {
            ContentView()
        }
    }
}

hi @masaichi ,

Can you please tell me which iOS version you are testing with? I tested with the Xcode 16.1 beta and iOS 18.0 and did not see the issue occur.

Hi Thank you for your reply. The environment I tested on below.

  • Xcode Version: 16.0 (16A242)
  • OS: iOS 18.0
  • Device: iPhone 16 Pro simulator, iPhone 12 mini

I could reproduce the issue with this sample code in both of the environments below.

  • Xcode 16.0 (16A242d), iOS 18.0 simulator
  • Xcode 16.1 beta (16B5001e), iOS 18.1 simulator

I was able to reproduce the issue with this sample code in both environments.

  • Xcode 16.0 (16A242d), iOS 18.0 simulator
  • Xcode 16.1 beta (16B5001e), iOS 18.1 simulator

I found that calling .simultaneousGesture(TapGesture()) on a Button can avoid this issue.

I'm having the same bug as well. But I encounter it when doing a long press, not a tap.

Adding simultaneousGesture for TapGesture/LongPressGesture didn't fix anything for me. But falling back to onTapGesture is "working".

Is anyone on apple side looking to this? This is a serious regression.

I'd like to second this issue. This is super annoying behavior causing endless unwanted touches to be executed when the user just wanted to scroll. Is this something that is actively looked into? Currently experiencing this issue under Xcode 16.2 iOS 18.2 both simulator and actual device.

I am experiencing the same issue. Xcode Version 16.2 (16C5032a) iOS 18.3. Using Button {} label: {} inside a scrollview. Having a VStack of buttons inside of a ScrollView makes the scrollview almost impossible to use without unintentional tapping.

The same bug on iOS 18.3 and Xcode 16.2.

I made "hack" for ScrollView to solve that problem. But it can conflict with your views if you used @Environment(\.isEnabled) var isEnabled


public struct FixScrollViewWithTappedButton: ViewModifier {

    @State private var isScrolling = false

    @ViewBuilder public func body(content: Content) -> some View {
        if #available(iOS 18, *) {
            content
                .disabled(isScrolling)
                .simultaneousGesture(
                    DragGesture()
                        .onChanged { _ in
                            isScrolling = true
                        }
                        .onEnded { _ in
                            isScrolling = false
                        }
                )
        }
    }
}

But my solution will reset your tap on button and you don't need to patch every button in your application.

Example:

 .sheet(isPresented: $isPresented) {
            ScrollView(showsIndicators: false) {
                VStack(alignment: .leading, spacing: 0) {
                    ForEach((0...31).map(String.init), id: \.self) { text in
                        Button(action: {
                            print("Tapped \(text)")
                        }) {
                            Text(text)
                                .padding()
                                .foregroundColor(.white)
                                .background(Color.blue)
                                .cornerRadius(8)
                        }
                    }
                }
                .modifier(FixScrollViewWithTappedButton())
            }
        }

We're seeing the same issue too. Xcode 16.3 iOS 18.4

I began to see this in our app too. It took me a while to figure out that it only affected modal views. Judging by the amount of time this thread has been open, I am going to guess that I am better off using one of the workarounds than waiting on a fix.

Changing from a Button to using onTapGesture { } for the subview resolves the issue for me, but you lose any button styling and tap state handling.

This is still happening in iOS 26 Beta 3, reported this via Feedback Assistant:

FB18927179: Button touch is not cancelled when sheet drag or scroll gesture starts on a Button inside a ScrollView inside a .sheet

Steps to reproduce:

  • Use the attached example snippet: A Button inside a ScrollView inside a .sheet
  • Start dragging the sheet or scrolling with the finger on the Button.

Expected behavior: After a little bit of drag movement, it should detect that it’s a scroll/sheet drag and the Button touch should be cancelled / the button action should not be fired. This behaviour can be observed when no ScrollView is present.

Observed behavior: Button touch is not cancelled and the action fires, even when the gesture clearly looked like a sheet drag/scroll.

Example code:

struct ContentView: View {
    @State var showAlert = false
    
    var body: some View {
        Text("Hello, world!")
            .sheet(isPresented: .constant(true)) {
                NavigationStack {
                    ScrollView(.vertical) {
                        VStack {
                            ForEach(1...100, id: \.self) { _ in
                                Button("Hello World") {
                                    self.showAlert = true
                                }
                            }
                        }
                        .frame(maxWidth: .infinity)
                        .buttonStyle(.borderedProminent)
                    }
                    .alert(isPresented: $showAlert) {
                        Alert(title: Text("Button tapped"))
                    }
                    .navigationTitle("Scrollable view in sheet")
                    .navigationBarTitleDisplayMode(.inline)
                }
            }
    }
}
Button Touch Not Canceled in ScrollView on Modal in SwiftUI for iOS 18
 
 
Q