Skip to content

Conversation

@grahammendick
Copy link
Owner

Swiping to scroll a horizontal list incorrectly triggered the full screen pop gesture. This conflict happened because the Navigation router was deciding when to begin the gesture instead of leaving it up to iOS. Tried various things to prevent the conflict but no luck (even the gesture experts at software mansion struggling with it). Ideally want the default pop gesture handling built into iOS. But implementing animationControllerForOperation in UINavigationControllerDelegate stops iOS from running its default pop gestures - even when returning nil.

First implemented animationControllerForOperation when introducing custom animations in #778. But custom animations in iOS are very rare and they don't even work that well in iOS 26 because iOS waits for them to complete before starting the next one (unlike the default animations), making them feel dated.

Changed to only implement animationControllerForOperation when custom animations are on. Created 2 UINavigationControllerDelegates - one without animationControllerForOperation and one with. Then choose which one to use based on the customAnimations prop. That way, 99% of the time (custom animations off) iOS handles the pop gestures and prevents the conflict with horizontal lists, for example.

The only way to opt into the default pop behavior is by not implementing animationControllerForOperation - as soon as implement it then never get the built-in pop gesture support. If don't get the built-in then can't get the ios26 full screen gesture properly - where it doesn't conflict with horizontal scroll view, for example. Tried returning NO/YES from various gesture recognizer stuff but can never get the non-conflict.
Plan to move the delegate off the NavigationStackView into separate classes. Then can add the right delegate depending on whether want custom animation or not. 99% of times there's no custom animation so add delegate without animationControllerForOperation and get default pop gestures
Created controller delegate that calls back to the view to handle. This allows registering different delegates for custom animations - so the 99% case doesn't implement animationControllerForOperation and so get the default ios gestures for popping
Swapped in the transition delegate and added custom animations and it works
Only want the delegate with animationControllerForOperation if custom animation otherwise can't fallback to default ios pop gestures - and end up with conflict with horizontal scroll. So added basic controller delegate if custom animation false and the transition one otherwise. Also only added the custom gesture recognizer if custom animation, too - the swipe from edge and full swipe - don't need them if not custom because ios handles it. And ios prevents the gesture conflict too - that's whole point of this work
Then only swap if changed in updateProps - does mean the default is setup pointlessly if custom animation on but that doesn't matter.
On Tweets made FlatList horizontal and changed from custom to none then couldn't swipe back from edge. When delegate set to nil then works for vertical scroll but not horizontal. Fixed by restoring the actual gesture recognizer delegate set by ios instead of setting to nil
No idea why, but on the old arch setting to nil didn't allow swipe back from edge on horizontal list. On the new arch, can swipe back. Think it's better to restore the original delegate used by ios than to set to nil anyway
Implemented the new custom animation property
@grahammendick grahammendick merged commit 9f5ee87 into master Oct 12, 2025
@grahammendick grahammendick deleted the gesture-conflict branch October 12, 2025 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants