Skip to content

Touch not cancelled on sheet drag

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.

Discussion: https://developer.apple.com/forums/thread/763436?answerId=849716022#849716022

Workaround:

swift
// » SwiftUI Garden
// » https://swiftui-garden.com/Misc/iOS-26/Touch-not-cancelled-on-sheet-drag

import SwiftUI

public extension View {
    // FB18927179: Button touch is not cancelled when sheet drag or scroll gesture starts
    // on a Button inside a ScrollView inside a .sheet, bug appeared in iOS 18, adding a
    // simultaneous TapGesture prevents the issue. It's enough to stick this once on the
    // outer container that is affected.
    // Thanks https://mastodon.social/@[email protected]
    // https://developer.apple.com/forums/thread/763436?answerId=849716022#849716022
    @available(iOS, deprecated: 26.1, message: "Check if this is still needed for iOS 26.1. If it is, bump this deprecation message to a higher version.")
    @ViewBuilder
    func iOS18ScrollViewButtonTapSheetFix() -> some View {
        if #available(iOS 18, *) {
            self.simultaneousGesture(TapGesture())
        } else {
            self
        }
    }
}

Example code:

swift
// » SwiftUI Garden
// » https://swiftui-garden.com/Misc/iOS-26/Touch-not-cancelled-on-sheet-drag

import SwiftUI

struct TouchNotCancelledOnSheetDragExample: 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)
                        // Workaround: .iOS18ScrollViewButtonTapSheetFix()
                    }
                    .alert(isPresented: $showAlert) {
                        Alert(title: Text("Button tapped"))
                    }
                    .navigationTitle("Scrollable view in sheet")
                    .navigationBarTitleDisplayMode(.inline)
                }
            }
    }
}

#Preview {
    TouchNotCancelledOnSheetDragExample()
}