ScrollView not working when DragGesture is enabled in subview
Asked Answered
G

2

3

Ever since Xcode 16, whenever there is a subview with a horizontal DragGesture inside a vertical ScrollView, horizontal drag gesture is detected on the subview but any scrolling up/down is not detected. Here's an example code that will not scroll but will recognize the horizontal gesture.

Both .gesture(DragGesture()) and .highPriorityGesture(DragGesture()) aren't working.

Row Item View

struct RowItem: View {
    let item: Int
    @State var offsetWidth: CGFloat = 0.0
    
    var body: some View {
        HStack {
            Text("Row \(item)")
            Spacer()
        }
        .padding()
        .background(Color.gray)
        .offset(x: offsetWidth)
        .gesture( // Issue starts here
            DragGesture()
                .onChanged { gesture in
                    let width = gesture.translation.width
                    
                    if -100..<0 ~= width {
                        if self.offsetWidth != -100 {
                            self.offsetWidth = width
                        }
                    } else if width < -100 {
                        self.offsetWidth = -100
                    }
                }
                .onEnded { _ in
                    if self.offsetWidth > -50 {
                        self.offsetWidth = .zero
                    } else {
                        self.offsetWidth = -100
                    }
                }
        )
        .onChange(of: offsetWidth) { newVal in
            Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { _ in
                withAnimation {
                    offsetWidth = 0.0
                }
            }
        }
    }
}

ScrollView

struct ExperimentView: View {
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(1...100, id: \.self) { i in
                    RowItem(item: i)
                    .background(Color.red)
                }
            }
        }
    }
}

Edit: This is also happening with List.

I tried using .simultaneousGesture(DragGesture()), but it causes the DragGesture to be recognized simultaneously with TapGesture, and it is not an ideal behaviour.

This particular issue is only noticed when the app is built on Xcode 16. It is not affecting an older version built on Xcode 15 that is running on iOS 18.

I found a thread on Apple's developer forum that talks about this but no fixes have been suggested.

General answered 19/9 at 5:33 Comment(3)
I have the same issue and I think this is a widespread problem since XCode 16/iOS 18, and there's surprisingly little information out there as to any changes to .highPriorityGesture compared to iOS 17 that would explain this breaking behaviour. I look forward to developments on this. I will update here if I find anything myself.Tsunami
It looks like the ScrollView takes a minimumDistance of 10.0 before it detects scrolling. Prior to Xcode 16, it seems the value was lower than the default value of 10.0 in DragGesture(). So, I tried using DragGesture(minimumDistance: 20.0) and it seems to work without affecting any functionality on my end. But it might not work for someone looking for a more sensitive threshold.General
Thanks for the update. I had tried playing with minimumDistance without success in my case. There was another thread on the topic where apparently a minimumDistance of 15 was the sweet spot. So, I am not sure minimumDistance is a reliable method. I think I just found out a way to fix it differently, working on putting together a demo.Tsunami
T
0

@Shri, I think i finally managed to figure out a proper fix for the gesture issues in iOS 18.

Thank you for taking the time to put together the reproducible example, I used it for testing and as the base for the demo below.

To recap, the issues were:

  1. Horizontal drag gestures on subviews inside a vertical scrolling view (ScrollView, List, etc.) when using .gesture and .highPriorityGesture modifiers are detected on the subview but any scrolling up/down (vertical) is not detected (if initial contact is within the area of the subview specifically).

  2. Using .simultaneousGesture(DragGesture()) (instead of .gesture or .highPriorityGesture restores vertical scrolling but causes the DragGesture() to be recognized simultaneously with TapGesture() causing both vertical and horizontal scrolling to occur at the same time - which is not ideal behaviour.

  3. Adding a .highPriorityGesture(DragGesture()) after the .simultaneousGesture(DragGesture()) breaks it again as described in point #1 above.

  4. Adding a .highPriorityGesture(TapGesture()) after the .simultaneousGesture(DragGesture()) has the same outcome as described in point #2 above.

  5. Specifiying a minimumDistance parameter for DragGesture() seemed to work in some cases, but inconsistently, making it an unreliable solution.

  6. This particular issue is only noticed when the app is built on Xcode 16. It is not affecting an older version built on Xcode 15 that is running on iOS 18.

  7. Similarly, this particular issue is also noticed when the app is built on Xcode 16 running on iOS 18, but NOT when app is built on Xcode 16 running iOS 17.5.


I spent a great deal of time trying various parameters like including, excluding for GestureMask for all gestures and their combinations, without success.

I tried using states and gesture states and applying gestures conditionally or using parameters like isEnabled, but no luck.

I did end up finding a working solution which required additional bindings to be passed around and some additional logic in the main view that would disable or enable scrolling based on initial point of contact and the direction of the gesture. That wasn't as flexible for my needs and although it was working, I wanted something simpler.

I had also previously tried a number of combinations with .simultaneousGesture and modifiers like .simultaneously, .sequenced and .exclusively, mostly around using DragGesture, but without the desired outcome.

That is, until I found one that worked:

.simultaneousGesture(dragGesture)
.highPriorityGesture(
    tapGesture
        .exclusively(before: dragGesture) 
)

I don't know how, given the number of combinations I tried previously, I didn't find this before, but it could be due to how I was using them (configured inline, within .simultaneousGesture, rather than a separate property as shown below).

So the solution steps are:

  1. Declare and configure the DragGesture individually, as a property (constant or variable depending on what makes sense for you)

  2. If your view also has logic for regular taps (as shown in the code below), do the same for TapGesture (configure it as property with whatever logic is needed).

  3. Add the drag gesture as a .simultaneousGesture modifier.

  4. Add the tap gesture as a .highPriorityGesture modifier using the .exclusively(before:) method so the tap happens exclusively before the drag gesture (some more notes on this below).

  5. Drink a beer to celebrate your app working as it did before.

Here's the full reproducible example that incorporates the fix:


import SwiftUI

//MARK: - Main content view

struct ExperimentGestureView: View {
    
    //State values
    @State private var sourceItem: Int?
    
    //Computed properties
    var status: String {
        if let sourceItem = sourceItem {
            return String(sourceItem)
        } else {
            return "None"
        }
    }
    
    //Body
    var body: some View {
        
        //Status
        Text("Tapped row: \(status)")
        
        ScrollView {
            LazyVStack {
                ForEach(1...20, id: \.self) { index in
                    
                    //Constant for varying row color - for beautification
                    let hue = Angle(degrees: Double(index) * 10)
                    
                    //Row view
                    ExperimentGestureRowItem(item: index, sourceItem: $sourceItem)
                        .hueRotation(hue)
                        .background(Color.red)
                        .clipShape(Capsule())
                }
            }
        }
        .resetOnScroll($sourceItem) //custom modifier for iOS 18+ that resets sourceItem on scroll
        .contentMargins(.horizontal, 40) //side padding to allow testing scrolling outside a row item
        .scrollIndicators(.hidden)
    }
}


//MARK: - Row item view

struct ExperimentGestureRowItem: View {
    
    //Parameters
    let item: Int
    @Binding var sourceItem: Int?
    
    //State values
    @State private var offsetWidth: CGFloat = 0.0
    @State private var itemID: Int?
    
    //Body
    var body: some View {
        
        //Drag gesture that reveals the background (and sets a binding identifying itself as the affected row
        let dragGesture = DragGesture()
            .onChanged { gesture in
                let width = gesture.translation.width
                
                if -100..<0 ~= width {
                    if self.offsetWidth != -100 {
                        self.offsetWidth = width
                    }
                } else if width < -100 {
                    self.offsetWidth = -100
                }
                
                //Update the binding to indicate the affected row/card/item
                sourceItem = item
            }
            .onEnded { _ in
                if self.offsetWidth > -50 {
                    self.offsetWidth = .zero
                } else {
                    self.offsetWidth = -100
                }
            }
        
        //Logic for simple tag gesture that resets offset if any row is tapped
        let tapGesture = TapGesture()
            .onEnded{
                withAnimation {
                    resetOffsetWidth()
                }
                sourceItem = item
            }
        
        //Layout
        HStack {
            Text("Row \(item)")
            Spacer()
        }
        .padding()
        .background(Color.teal)
        .foregroundStyle(Color.white)
        .clipShape(Capsule())
        .offset(x: offsetWidth)
        .simultaneousGesture(dragGesture)
        .highPriorityGesture(
            tapGesture
                .exclusively(before: dragGesture) // <- Here, this is needed to restore desired scrolling behaviour
        )
        .onChange(of: sourceItem) {oldValue, newValue in
            if newValue == nil || newValue != item {
                withAnimation {
                    resetOffsetWidth()
                }
            }
        }
    }
    
    //Convenience function for resetting offset
    private func resetOffsetWidth() {
        self.offsetWidth = .zero
    }
}


//MARK: - View extension

extension View {
    
    //Modifier conditionally applied for iOS 18+ that resets the object passed as parameter on scroll
    func resetOnScroll<T>(_ binding: Binding<T?>) -> some View {
        Group {
            if #available(iOS 18.0, *) {
                self
                    .onScrollPhaseChange({ _, newPhase in
                        binding.wrappedValue = nil
                    })
            }
            else {
                self
            }
        }
    }
}


//MARK: - Preview

#Preview {
    ExperimentGestureView()
}

Notes:

  • The solution above is based around the reproducible example you provided, plus some minor bells and whistles.

  • Added a couple of states and parameters to allow for the offset to be reset when clicking the row, clicking any other row or dragging another row.

  • Added some padding on the sides for testing (since vertical scrolling before did work if initial drag started outside the area of the row)

  • Added some color variation for visual gratification

  • Optionally, and to bring it more inline with how horizontal swiping works in system-wide, like swiping in list of conversations in Messages, I used the new .onScrollPhaseChange of iOS 18 to reset the offset as soon as the page is scrolled. This modifier is added as a view extension and applied only if iOS 18 is available, which allows the very same code to also work on iOS 17+ (and maybe older versions, not tested).

  • It's important for gestures to be declared separately, outside of the respective modifiers like .simultaneousGesture and .highPriorityGesture, so they can be referenced and used as shown. The same applies to any regular tap gestures that you may have now added using .onTapGesture - primarily because the .highPriorityGesture, unless used as shown, may break functionality that would otherwise work if defined in a .onTapGesture.

Below is an example regarding the last point. In the following code, the logic to reset the row offset with a single tap on any row is added using the .onTapGesture modifier:

.simultaneousGesture(dragGesture)
.highPriorityGesture(
    TapGesture()
        .exclusively(before: dragGesture) 
)
.onTapGesture {
    withAnimation {
        resetOffsetWidth()
    }
    sourceItem = item
}

This, however, will cause the reset on tap to break, since the high priority gesture will replace the logic defined via .onTapGesture. The scrolling will work as intended but the tap to reset will not.


That's about it, let me know if this works out for you.

enter image description here

Tsunami answered 29/9 at 22:44 Comment(10)
It seems that this isn't the first time there were issues with drag gestures in ScrollView. I came across a thread from 5 years or so go, the iOS 13 era, where similar symptoms and possible solutions were explored. #gesturesgate ?Tsunami
Thanks for this detailed answer, Andrei. I've tested your code and it works brilliantly. However, I noticed it seems to block the subviews that require user interaction. Would you mind trying to replace your item view (the HStack part) with the following code (which I'll paste in a separate comment due to the word limit) to see if the button and text respond to tapping?Northeasterly
``` VStack { Text("Row (item)") Button("Tap Me") { print("Button tapped") } .padding() .background(Color.blue) .foregroundColor(.white) Text("Other tappable content").onTapGesture { print("content tapped") } } .frame(maxWidth: .infinity) ```Northeasterly
Sorry, hard returns don't work in comments.Northeasterly
Anyway, your solution looks great because I think one potential approach is to delegate all subview interactions to that tap gesture. However, in my case, that's hardly an option as the subviews are highly versatile and dynamic.Northeasterly
Try changing the .highPriorityGesture to just .gesture and see if it works with your codeTsunami
It doesn't work, and is worse than highPriorityGesture, as now the horizontal drag gesture won't work at all on top of subview buttons.Northeasterly
I updated the HStack in item view with the code you provided and changed to gesture as mentioned, and everything seems to work on my end, including the horizontal drag gesture.Tsunami
VStack { Text("Row (item)") Button("Tap Me") { print("Button tapped") } .padding() .background(Color.blue) .foregroundColor(.white) Text("Other tappable content") .onTapGesture { print("content tapped") } } .frame(maxWidth: .infinity) .padding() .background(Color.teal) .foregroundStyle(Color.white) .clipShape(Capsule()) .offset(x: offsetWidth) .simultaneousGesture(dragGesture) .gesture( tapGesture .exclusively(before: dragGesture) )Tsunami
Thanks, Andrei, I really appreciate your help here. I replaced your code, and everything else works well except when dragging from the button. The "Button tapped" is still triggered after the horizontal gesture ends. Here is my testing code as well as a recording showing my operations. gist.github.com/Eric0625/23bfca080eb2fa288c7116ffcd0549c4Northeasterly
G
1

After trying different things, the only way that didn't require major changes on my app was to use DragGesture(minimumDistance: 20.0). I tried different values and it seems that values up to minimumDistance: 15 don't work correctly.

The documentation for DragGesture is as follows:

@MainActor @preconcurrency
init(
    minimumDistance: CGFloat = 10,
    coordinateSpace: some CoordinateSpaceProtocol = .local
)

So, it is likely that ScrollView in SwiftUI 6/Xcode 16 has a similar threshold for detecting "scrolling". In previous versions of SwiftUI, it seems that ScrollView always had a lower threshold than the default value for DragGesture.

The following snippet of code (with minimumDistance: 20.0) seems to work for my use case. I also tried minimumDistance: 15.0 but it seemed to randomly choose between scroll or drag.

Edit: Code snippet updated to a reproducible example.

struct RowItem: View {
    let item: Int
    @State var offsetWidth: CGFloat = 0.0
    
    var body: some View {
        HStack {
            Text("Row \(item)")
            Spacer()
        }
        .padding()
        .background(Color.gray)
        .offset(x: offsetWidth)
        .gesture(
            DragGesture(minimumDistance: 20) // Added minimumDistance
                .onChanged { gesture in
                    let width = gesture.translation.width
                    
                    if -100..<0 ~= width {
                        if self.offsetWidth != -100 {
                            self.offsetWidth = width
                        }
                    } else if width < -100 {
                        self.offsetWidth = -100
                    }
                }
                .onEnded { _ in
                    if self.offsetWidth > -50 {
                        self.offsetWidth = .zero
                    } else {
                        self.offsetWidth = -100
                    }
                }
        )
        .onChange(of: offsetWidth) { newVal in
            Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { _ in
                withAnimation {
                    offsetWidth = 0.0
                }
            }
        }
    }
}

Scroll View

    struct ExperimentView: View {
        var body: some View {
            ScrollView {
                LazyVStack {
                    ForEach(1...100, id: \.self) { i in
                        RowItem(item: i)
                        .background(Color.red)
                    }
                }
            }
        }
    }

Note: Using simultaneousGesture also works but it requires proper handling of TapGesture. If not done correctly, the Tap and Drag are triggered simultaneously.

General answered 19/9 at 20:27 Comment(3)
It would be helpful if both your original code and the one in the answer were reproducible, so others can run tests on them. In my case, it would help to use your reproducible non working code to test a different method for fixing this issue.Tsunami
Thanks for the suggestion @AndreiG. I have updated the answer to include a fully reproducible code. It gives different behaviour with different values of minimumDistance.General
Thanks for providing the full reproducible code. I added an answer that is hopefully a reliable fix. Give it a try and let me know if it works.Tsunami
T
0

@Shri, I think i finally managed to figure out a proper fix for the gesture issues in iOS 18.

Thank you for taking the time to put together the reproducible example, I used it for testing and as the base for the demo below.

To recap, the issues were:

  1. Horizontal drag gestures on subviews inside a vertical scrolling view (ScrollView, List, etc.) when using .gesture and .highPriorityGesture modifiers are detected on the subview but any scrolling up/down (vertical) is not detected (if initial contact is within the area of the subview specifically).

  2. Using .simultaneousGesture(DragGesture()) (instead of .gesture or .highPriorityGesture restores vertical scrolling but causes the DragGesture() to be recognized simultaneously with TapGesture() causing both vertical and horizontal scrolling to occur at the same time - which is not ideal behaviour.

  3. Adding a .highPriorityGesture(DragGesture()) after the .simultaneousGesture(DragGesture()) breaks it again as described in point #1 above.

  4. Adding a .highPriorityGesture(TapGesture()) after the .simultaneousGesture(DragGesture()) has the same outcome as described in point #2 above.

  5. Specifiying a minimumDistance parameter for DragGesture() seemed to work in some cases, but inconsistently, making it an unreliable solution.

  6. This particular issue is only noticed when the app is built on Xcode 16. It is not affecting an older version built on Xcode 15 that is running on iOS 18.

  7. Similarly, this particular issue is also noticed when the app is built on Xcode 16 running on iOS 18, but NOT when app is built on Xcode 16 running iOS 17.5.


I spent a great deal of time trying various parameters like including, excluding for GestureMask for all gestures and their combinations, without success.

I tried using states and gesture states and applying gestures conditionally or using parameters like isEnabled, but no luck.

I did end up finding a working solution which required additional bindings to be passed around and some additional logic in the main view that would disable or enable scrolling based on initial point of contact and the direction of the gesture. That wasn't as flexible for my needs and although it was working, I wanted something simpler.

I had also previously tried a number of combinations with .simultaneousGesture and modifiers like .simultaneously, .sequenced and .exclusively, mostly around using DragGesture, but without the desired outcome.

That is, until I found one that worked:

.simultaneousGesture(dragGesture)
.highPriorityGesture(
    tapGesture
        .exclusively(before: dragGesture) 
)

I don't know how, given the number of combinations I tried previously, I didn't find this before, but it could be due to how I was using them (configured inline, within .simultaneousGesture, rather than a separate property as shown below).

So the solution steps are:

  1. Declare and configure the DragGesture individually, as a property (constant or variable depending on what makes sense for you)

  2. If your view also has logic for regular taps (as shown in the code below), do the same for TapGesture (configure it as property with whatever logic is needed).

  3. Add the drag gesture as a .simultaneousGesture modifier.

  4. Add the tap gesture as a .highPriorityGesture modifier using the .exclusively(before:) method so the tap happens exclusively before the drag gesture (some more notes on this below).

  5. Drink a beer to celebrate your app working as it did before.

Here's the full reproducible example that incorporates the fix:


import SwiftUI

//MARK: - Main content view

struct ExperimentGestureView: View {
    
    //State values
    @State private var sourceItem: Int?
    
    //Computed properties
    var status: String {
        if let sourceItem = sourceItem {
            return String(sourceItem)
        } else {
            return "None"
        }
    }
    
    //Body
    var body: some View {
        
        //Status
        Text("Tapped row: \(status)")
        
        ScrollView {
            LazyVStack {
                ForEach(1...20, id: \.self) { index in
                    
                    //Constant for varying row color - for beautification
                    let hue = Angle(degrees: Double(index) * 10)
                    
                    //Row view
                    ExperimentGestureRowItem(item: index, sourceItem: $sourceItem)
                        .hueRotation(hue)
                        .background(Color.red)
                        .clipShape(Capsule())
                }
            }
        }
        .resetOnScroll($sourceItem) //custom modifier for iOS 18+ that resets sourceItem on scroll
        .contentMargins(.horizontal, 40) //side padding to allow testing scrolling outside a row item
        .scrollIndicators(.hidden)
    }
}


//MARK: - Row item view

struct ExperimentGestureRowItem: View {
    
    //Parameters
    let item: Int
    @Binding var sourceItem: Int?
    
    //State values
    @State private var offsetWidth: CGFloat = 0.0
    @State private var itemID: Int?
    
    //Body
    var body: some View {
        
        //Drag gesture that reveals the background (and sets a binding identifying itself as the affected row
        let dragGesture = DragGesture()
            .onChanged { gesture in
                let width = gesture.translation.width
                
                if -100..<0 ~= width {
                    if self.offsetWidth != -100 {
                        self.offsetWidth = width
                    }
                } else if width < -100 {
                    self.offsetWidth = -100
                }
                
                //Update the binding to indicate the affected row/card/item
                sourceItem = item
            }
            .onEnded { _ in
                if self.offsetWidth > -50 {
                    self.offsetWidth = .zero
                } else {
                    self.offsetWidth = -100
                }
            }
        
        //Logic for simple tag gesture that resets offset if any row is tapped
        let tapGesture = TapGesture()
            .onEnded{
                withAnimation {
                    resetOffsetWidth()
                }
                sourceItem = item
            }
        
        //Layout
        HStack {
            Text("Row \(item)")
            Spacer()
        }
        .padding()
        .background(Color.teal)
        .foregroundStyle(Color.white)
        .clipShape(Capsule())
        .offset(x: offsetWidth)
        .simultaneousGesture(dragGesture)
        .highPriorityGesture(
            tapGesture
                .exclusively(before: dragGesture) // <- Here, this is needed to restore desired scrolling behaviour
        )
        .onChange(of: sourceItem) {oldValue, newValue in
            if newValue == nil || newValue != item {
                withAnimation {
                    resetOffsetWidth()
                }
            }
        }
    }
    
    //Convenience function for resetting offset
    private func resetOffsetWidth() {
        self.offsetWidth = .zero
    }
}


//MARK: - View extension

extension View {
    
    //Modifier conditionally applied for iOS 18+ that resets the object passed as parameter on scroll
    func resetOnScroll<T>(_ binding: Binding<T?>) -> some View {
        Group {
            if #available(iOS 18.0, *) {
                self
                    .onScrollPhaseChange({ _, newPhase in
                        binding.wrappedValue = nil
                    })
            }
            else {
                self
            }
        }
    }
}


//MARK: - Preview

#Preview {
    ExperimentGestureView()
}

Notes:

  • The solution above is based around the reproducible example you provided, plus some minor bells and whistles.

  • Added a couple of states and parameters to allow for the offset to be reset when clicking the row, clicking any other row or dragging another row.

  • Added some padding on the sides for testing (since vertical scrolling before did work if initial drag started outside the area of the row)

  • Added some color variation for visual gratification

  • Optionally, and to bring it more inline with how horizontal swiping works in system-wide, like swiping in list of conversations in Messages, I used the new .onScrollPhaseChange of iOS 18 to reset the offset as soon as the page is scrolled. This modifier is added as a view extension and applied only if iOS 18 is available, which allows the very same code to also work on iOS 17+ (and maybe older versions, not tested).

  • It's important for gestures to be declared separately, outside of the respective modifiers like .simultaneousGesture and .highPriorityGesture, so they can be referenced and used as shown. The same applies to any regular tap gestures that you may have now added using .onTapGesture - primarily because the .highPriorityGesture, unless used as shown, may break functionality that would otherwise work if defined in a .onTapGesture.

Below is an example regarding the last point. In the following code, the logic to reset the row offset with a single tap on any row is added using the .onTapGesture modifier:

.simultaneousGesture(dragGesture)
.highPriorityGesture(
    TapGesture()
        .exclusively(before: dragGesture) 
)
.onTapGesture {
    withAnimation {
        resetOffsetWidth()
    }
    sourceItem = item
}

This, however, will cause the reset on tap to break, since the high priority gesture will replace the logic defined via .onTapGesture. The scrolling will work as intended but the tap to reset will not.


That's about it, let me know if this works out for you.

enter image description here

Tsunami answered 29/9 at 22:44 Comment(10)
It seems that this isn't the first time there were issues with drag gestures in ScrollView. I came across a thread from 5 years or so go, the iOS 13 era, where similar symptoms and possible solutions were explored. #gesturesgate ?Tsunami
Thanks for this detailed answer, Andrei. I've tested your code and it works brilliantly. However, I noticed it seems to block the subviews that require user interaction. Would you mind trying to replace your item view (the HStack part) with the following code (which I'll paste in a separate comment due to the word limit) to see if the button and text respond to tapping?Northeasterly
``` VStack { Text("Row (item)") Button("Tap Me") { print("Button tapped") } .padding() .background(Color.blue) .foregroundColor(.white) Text("Other tappable content").onTapGesture { print("content tapped") } } .frame(maxWidth: .infinity) ```Northeasterly
Sorry, hard returns don't work in comments.Northeasterly
Anyway, your solution looks great because I think one potential approach is to delegate all subview interactions to that tap gesture. However, in my case, that's hardly an option as the subviews are highly versatile and dynamic.Northeasterly
Try changing the .highPriorityGesture to just .gesture and see if it works with your codeTsunami
It doesn't work, and is worse than highPriorityGesture, as now the horizontal drag gesture won't work at all on top of subview buttons.Northeasterly
I updated the HStack in item view with the code you provided and changed to gesture as mentioned, and everything seems to work on my end, including the horizontal drag gesture.Tsunami
VStack { Text("Row (item)") Button("Tap Me") { print("Button tapped") } .padding() .background(Color.blue) .foregroundColor(.white) Text("Other tappable content") .onTapGesture { print("content tapped") } } .frame(maxWidth: .infinity) .padding() .background(Color.teal) .foregroundStyle(Color.white) .clipShape(Capsule()) .offset(x: offsetWidth) .simultaneousGesture(dragGesture) .gesture( tapGesture .exclusively(before: dragGesture) )Tsunami
Thanks, Andrei, I really appreciate your help here. I replaced your code, and everything else works well except when dragging from the button. The "Button tapped" is still triggered after the horizontal gesture ends. Here is my testing code as well as a recording showing my operations. gist.github.com/Eric0625/23bfca080eb2fa288c7116ffcd0549c4Northeasterly

© 2022 - 2024 — McMap. All rights reserved.