SwiftUI List onTapGesture Not Working After iOS 18 Update: Solutions and Fixes

In this post, we will explore potential fixes for the issue where SwiftUI List elements fail to register onTapGesture actions after upgrading to iOS 18. The code worked perfectly fine over the past few months but now behaves inconsistently on iPhones running iOS 18. While holding down a button or element triggers the tapGesture action as expected, a quick tap often does not. Occasionally, the onTapGesture is recognized, but it is not reliable.

I encountered an issue where a button inside a List and its onTapGesture stopped responding after upgrading to iOS 18. After researching the problem, I discovered that if a parent view has a TapGesture applied, it can override the TapGesture of its child views. To address this, there are a few solutions available. Let’s explore them.

Adding highPriority TapGesture

We can resolve this issue by adding a high-priority TapGesture to the child view. This method works effectively if the child view has only a single TapGesture applied.

.highPriorityGesture(
    TapGesture().onEnded { _ in
        // your code here to handle tap gesture......
    }
)

Replace parentView’s .onTapGesture with .simultaneousGesture

In my case, I had both a button and a TapGesture on the child view, while the parent view also had a TapGesture to dismiss the keyboard. Using a high-priority TapGesture was not a viable solution because it overrode the previously applied TapGesture actions, causing all button links to trigger the action defined in the last TapGesture. Instead, I used simultaneousGesture and applied it to the parent view. Below is an example:

   let newGesture = TapGesture().onEnded {
            UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder),
                                            to: nil, from: nil, for: nil)
    }

    var body: some View {
        VStack(spacing:25) {
            Text("Child view goes here")
            Rectangle()
                .fill(Color.blue)
        }
        .simultaneousGesture(newGesture)
        .frame(width: 200, height: 200)
        .border(Color.purple)
    }
}