Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prioritise UITapGestureRecognizer on interop views with Cooperative interop mode. Prioritise UIScreenEdgePanGestureRecognizer on ascendant views. #1695

Merged
merged 2 commits into from
Dec 11, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import platform.CoreGraphics.CGPointMake
import platform.CoreGraphics.CGRectZero
import platform.UIKit.UIEvent
import platform.UIKit.UIGestureRecognizer
import platform.UIKit.UITapGestureRecognizer
import platform.UIKit.UIScreenEdgePanGestureRecognizer
import platform.UIKit.UIGestureRecognizerDelegateProtocol
import platform.UIKit.UIGestureRecognizerState
import platform.UIKit.UIGestureRecognizerStateBegan
Expand Down Expand Up @@ -135,13 +137,19 @@ private class UserInputGestureRecognizerDelegateProxy : CMPGestureRecognizerDele
): Boolean {
// We should recognize simultaneously only with the gesture recognizers
// belonging to itself or to the views up in the hierarchy.
// An exception: UIScreenEdgePanGestureRecognizer, this always has precedence over us and is
// not allowed to recognize simultaneously

// Can't check if either view is null
// Can't proceed if either view is null
val view = gestureRecognizer.view ?: return false
val otherView = otherGestureRecognizer.view ?: return false

val otherIsAscendant = !otherView.isDescendantOfView(view)

if (otherIsAscendant && otherGestureRecognizer is UIScreenEdgePanGestureRecognizer) {
return false
}

// Only allow simultaneous recognition if the other gesture recognizer is attached to the same view
// or to a view up in the hierarchy
return otherView == view || otherIsAscendant
Expand All @@ -151,24 +159,55 @@ private class UserInputGestureRecognizerDelegateProxy : CMPGestureRecognizerDele
gestureRecognizer: UIGestureRecognizer,
otherGestureRecognizer: UIGestureRecognizer
): Boolean {
// We don't require other gesture recognizers to fail.
// Assumption is that we recognize
// simultaneously with the gesture recognizers of the views up in the hierarchy.
// And gesture recognizers down the hierarchy require to failure us.
// Two situations are possible here.
// 1. If it's a gesture recognizer of a descendant (interop) view,
// we should wait until it fails,
// if it's a UITapGestureRecognizer.
//
// 2. It's a gesture recognizer of the view itself, or it's an ascendant view.
// We don't require failure of it, unless it's a `UIScreenEdgePanGestureRecognizer`.

val view = gestureRecognizer.view ?: return false
val otherView = otherGestureRecognizer.view ?: return false

val otherIsDescendant = otherView.isDescendantOfView(view)
val otherIsAscendantOrSameView = !otherIsDescendant

// (1)
if (otherIsDescendant && otherGestureRecognizer is UITapGestureRecognizer) {
return true
}

// (2)
if (otherIsAscendantOrSameView && otherGestureRecognizer is UIScreenEdgePanGestureRecognizer) {
return true
}

return false
}

override fun gestureRecognizerShouldBeRequiredToFailByGestureRecognizer(
gestureRecognizer: UIGestureRecognizer,
otherGestureRecognizer: UIGestureRecognizer
): Boolean {
// Other gesture recognizers,
// except the case where it belongs to the same view,
// are required to wait until we fail.
// In practice, it can only happen when other gesture recognizers are attached to the
// descendant views (aka interop views).
// In other cases, it's allowed to recognize simultaneously, so this method will not be
// called

// otherGestureRecognizer is UITapGestureRecognizer,
// it must not wait till we fail and has priority
if (otherGestureRecognizer is UITapGestureRecognizer) {
return false
}

val view = gestureRecognizer.view ?: return false
val otherView = otherGestureRecognizer.view ?: return false

val otherIsDescendant = otherView.isDescendantOfView(view)
val otherIsAscendantOrSameView = !otherIsDescendant

if (otherIsAscendantOrSameView && otherGestureRecognizer is UIScreenEdgePanGestureRecognizer) {
return false
}

// Otherwise it is required to fail (aka other kind of gesture recognizer on interop view)
return gestureRecognizer.view != otherGestureRecognizer.view
}
}
Expand Down
Loading