Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[ios][platform_view] Fix Platform view gesture recognizer with iPad pencil getting stuck #55724

Conversation

hellohuanlin
Copy link
Contributor

@hellohuanlin hellohuanlin commented Oct 8, 2024

Summary

I came across our the "gesture recognizer delegate" implementation and it is quite odd (see below). After fixing it, the problem is resolved. However, it's hard to reason about how it's related to iPad pencil, since it's internal logic that we don't know (see my research below).

Gesture recognizer delegate

Existing odd implementation

  • shouldBeRequiredToFailByGestureRecognizer:

otherGestureRecognizer != self is always YES because the delegate set to self, hence gestureRecognizer must be self, hence otherGestureRecognizer must not be self.

  • shouldRequireFailureOfGestureRecognizer:

otherGestureRecognizer == self is always NO, for the same reason described above.

new implementation:

After digging into various PRs, the idea seems to be that we want to have a precedence of "Forwarding recognizer > Delaying recognizer > Other recognizers in platform view".

  • shouldBeRequiredToFailByGestureRecognizer:

return otherGestureRecognizer != _forwardingRecognizer means Delaying recognizer needs to be higher precedence than all non-Forwarding recognizer. (aka "Delaying recognizer > Other recognizers in platform view")

  • shouldRequireFailureOfGestureRecognizer:

return otherGestureRecognizer == _forwardingRecognizer means Delaying recognizer needs to have lower precedence than forwarding recognizer. (aka "Forwarding recognizer > Delaying recognizer").

Some research

This is a tricky one since pencil and finger triggers exactly the same callbacks. It turns out that when pencil is involved after finger interaction, the platform view's "forwarding" gesture recognizer is stuck at failed state. This seems to be an iOS bug, because according to the API doc, it should be reset back to "possible" state:

No action message is sent and the gesture recognizer is reset to UIGestureRecognizerStatePossible.

However, when iPad pencil is involved, the state is not reset. I tried to KVO the state property, and wasn't able to capture the change. This means the state change very likely happened internally within the recognizer via the backing ivar of the state property.

List which issues are fixed by this PR. You must list at least one issue.

Fixes flutter/flutter#136244

If you had to change anything in the flutter/tests repo, include a link to the migration guide as per the breaking change policy.

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide and the C++, Objective-C, Java style guides.
  • I listed at least one issue that this PR fixes in the description above.
  • I added new tests to check the change I am making or feature I am adding, or the PR is test-exempt. See testing the engine for instructions on writing and running engine tests.
  • I updated/added relevant documentation (doc comments with ///).
  • I signed the CLA.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@@ -654,14 +654,14 @@ - (instancetype)initWithTarget:(id)target

- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
// The forwarding gesture recognizer should always get all touch events, so it should not be
// required to fail by any other gesture recognizer.
return otherGestureRecognizer != _forwardingRecognizer && otherGestureRecognizer != self;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we set self as the delegate, otherGestureRecognizer != self is always YES.

@hellohuanlin hellohuanlin requested a review from jmagman October 8, 2024 04:04
@hellohuanlin
Copy link
Contributor Author

I can confirm this fixed the issue, but need to make sure it doesn't break anything

@jmagman
Copy link
Member

jmagman commented Oct 8, 2024

Logic introduced in #6665
Can you try running this on the framework tests, hopefully the gesture recognizer tests can run on it https://github.com/flutter/engine/blob/main/docs/Testing-presubmit-Engine-PRs-with-the-Flutter-framework.md

@hellohuanlin hellohuanlin added the test: all See https://github.com/flutter/engine/blob/main/docs/ci/Engine-pre-submits-and-post-submits.md label Oct 8, 2024
@hellohuanlin
Copy link
Contributor Author

hellohuanlin commented Oct 8, 2024

Running the presubmit test now.

I also manually tested a few scenario:

  1. Scrollable list of ads are clickable
  2. Non scrollable list of ads are clickable
  3. Scrollable list of web views are clickable, but not zoomable or scrollable (since flutter's scrolling list deals with the gesture)
  4. Non scrollable list of web views are clickable, and also zoomable and scrollable (since flutter's doesn't deal with the gesture, so touch events gets propagated into the web view)
  5. A simple platform view of UIButton's callback is still called

Also iPad pencil doesn't break the gestures in above examples.

@hellohuanlin
Copy link
Contributor Author

hellohuanlin commented Oct 9, 2024

Tested the webview package's example app -

  • Finger zooming, scrolling, clicking still works.
  • Scrolling using iPad pencil also works.
  • Zooming with finger + pencil doesn't stuck.

@hellohuanlin
Copy link
Contributor Author

hellohuanlin commented Oct 9, 2024

Tested on google map package's example app (this app is pretty well made and it covers lots of gesture functionalities, so it gives me lots of confidence). I tried all the following features related to touch/gesture:

  • Under "user interface" page, i tried disable/enable zooming and scrolling.
  • Under "Map click" page, long press and tap gesture works
  • Under "Scrolling map" page:
    a. Able to scroll the list by dragging in regions outside of both maps. Tried both finger and pencil.
    b. The first map consumes all touch events. So dragging inside this map won't scroll the list, as expected;
    c. The second map consumes vertical drags but still get other gestures. So dragging inside it scrolls the flutter list as expected; and zooming and tapping on the map still works (able to interact with the map); zooming with finger first and then pencil won't stuck.

@hellohuanlin
Copy link
Contributor Author

Tried web view example again with the main branch - confirmed same behavior as expected.

@hellohuanlin
Copy link
Contributor Author

Tried google map example again with the main branch - also same behavior as expected.

@hellohuanlin
Copy link
Contributor Author

Verified the regression in previous workaround (UIButton's callback not being called) doesn't happen in this PR.

@hellohuanlin hellohuanlin force-pushed the pv_ipad_pencil_touch_dynamic_failure_condition branch from 66eb926 to 7381f37 Compare October 10, 2024 02:33
@hellohuanlin hellohuanlin marked this pull request as ready for review October 10, 2024 07:28
@jmagman jmagman requested a review from cbracken October 10, 2024 17:20
Copy link
Member

@jmagman jmagman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have gesture integration tests? Can we add something to scenario app to test some of the manual tests you did?

@hellohuanlin hellohuanlin changed the title [DRAFT][ios][platform_view] Fix Platform view gesture recognizer with iPad pencil getting stuck [ios][platform_view] Fix Platform view gesture recognizer with iPad pencil getting stuck Oct 10, 2024
@hellohuanlin
Copy link
Contributor Author

hellohuanlin commented Oct 10, 2024

Do we have gesture integration tests? Can we add something to scenario app to test some of the manual tests you did?

For this particular bug: I don't think it's possible to mimic ipad pencil interaction in XCUITests. I couldn't find any suitable API (e.g. XCUIElement APIs).

The manual tests I did was to ensure we don't regress our general gesture logic (unfortunately they won't help catch this bug). We don't have any of those tests, but I think we should add those to improve our code quality. That would be a pretty big project though, so I'm leaning towards having it as a separate project. I can create a ticket maybe for next year. WDYT? @jmagman

I don't think Scenario test will do - we need the framework part as well (e.g. to check if GestureDetector widget gets the callbacks).

Copy link
Member

@jmagman jmagman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The manual tests I did was to ensure we don't regress our general gesture logic (unfortunately they won't help catch this bug). We don't have any of those tests, but I think we should add those to improve our code quality. That would be a pretty big project though, so I'm leaning towards having it as a separate project. I can create a ticket maybe for next year. WDYT?

That would be really helpful, thank you!

LGTM let's try it.

@hellohuanlin hellohuanlin added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 11, 2024
@auto-submit auto-submit bot merged commit e41a762 into flutter:main Oct 11, 2024
36 checks passed
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Oct 11, 2024
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Oct 11, 2024
…156562)

flutter/engine@3f0e2fb...e41a762

2024-10-11 41930132+hellohuanlin@users.noreply.github.com [ios][platform_view] Fix Platform view gesture recognizer with iPad pencil getting stuck (flutter/engine#55724)
2024-10-11 flar@google.com [DisplayList] Create DlFoo type variants to start moving away from Skia types in API (flutter/engine#55812)
2024-10-10 jonahwilliams@google.com [engine] remove merge thread setting and fix default value. (flutter/engine#55810)
2024-10-10 skia-flutter-autoroll@skia.org Roll Skia from 1e269594df9d to 97cebfb06139 (3 revisions) (flutter/engine#55811)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC jacksongardner@google.com,zra@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
auto-submit bot added a commit to flutter/flutter that referenced this pull request Oct 11, 2024
…isions) (#156562)" (#156594)

Reverts: #156562
Initiated by: zanderso
Reason for reverting: Linux_mokey flutter_gallery__back_button_memory is failing repeatedly on framework CI
Original PR Author: engine-flutter-autoroll

Reviewed By: {fluttergithubbot}

This change reverts the following previous change:

flutter/engine@3f0e2fb...e41a762

2024-10-11 41930132+hellohuanlin@users.noreply.github.com [ios][platform_view] Fix Platform view gesture recognizer with iPad pencil getting stuck (flutter/engine#55724)
2024-10-11 flar@google.com [DisplayList] Create DlFoo type variants to start moving away from Skia types in API (flutter/engine#55812)
2024-10-10 jonahwilliams@google.com [engine] remove merge thread setting and fix default value. (flutter/engine#55810)
2024-10-10 skia-flutter-autoroll@skia.org Roll Skia from 1e269594df9d to 97cebfb06139 (3 revisions) (flutter/engine#55811)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC jacksongardner@google.com,zra@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
auto-submit bot added a commit to flutter/flutter that referenced this pull request Oct 11, 2024
…4a (4 revisions) (#156562)" (#156594)" (#156597)

Reverts: #156594
Initiated by: jonahwilliams
Reason for reverting: workaround landed?
Original PR Author: auto-submit[bot]

Reviewed By: {fluttergithubbot}

This change reverts the following previous change:
Reverts: #156562
Initiated by: zanderso
Reason for reverting: Linux_mokey flutter_gallery__back_button_memory is failing repeatedly on framework CI
Original PR Author: engine-flutter-autoroll

Reviewed By: {fluttergithubbot}

This change reverts the following previous change:

flutter/engine@3f0e2fb...e41a762

2024-10-11 41930132+hellohuanlin@users.noreply.github.com [ios][platform_view] Fix Platform view gesture recognizer with iPad pencil getting stuck (flutter/engine#55724)
2024-10-11 flar@google.com [DisplayList] Create DlFoo type variants to start moving away from Skia types in API (flutter/engine#55812)
2024-10-10 jonahwilliams@google.com [engine] remove merge thread setting and fix default value. (flutter/engine#55810)
2024-10-10 skia-flutter-autoroll@skia.org Roll Skia from 1e269594df9d to 97cebfb06139 (3 revisions) (flutter/engine#55811)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC jacksongardner@google.com,zra@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
@hellohuanlin hellohuanlin added the revert Label used to revert changes in a closed and merged pull request. label Oct 15, 2024
Copy link
Contributor

auto-submit bot commented Oct 15, 2024

Time to revert pull request flutter/engine/55724 has elapsed.
You need to open the revert manually and process as a regular pull request.

@auto-submit auto-submit bot removed the revert Label used to revert changes in a closed and merged pull request. label Oct 15, 2024
hellohuanlin added a commit to hellohuanlin/engine that referenced this pull request Oct 15, 2024
auto-submit bot pushed a commit that referenced this pull request Oct 16, 2024
#55889)

�h iPad pencil getting stuck (#55724)"

This reverts commit e41a762.

Revert since a customer reported the platform view touch is broken after the change. 

*List which issues are fixed by this PR. You must list at least one issue.*
Reopens #55724

*If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
nick9822 pushed a commit to nick9822/flutter that referenced this pull request Dec 18, 2024
…encil getting stuck (flutter/engine#55724)

## Summary 

I came across our the "gesture recognizer delegate" implementation and it is quite odd (see below). After fixing it, the problem is resolved. However, it's hard to reason about how it's related to iPad pencil, since it's internal logic that we don't know (see my research below). 

## Gesture recognizer delegate

### Existing odd implementation

- shouldBeRequiredToFailByGestureRecognizer: 

`otherGestureRecognizer != self` is always YES because the delegate set to self, hence `gestureRecognizer` must be self, hence `otherGestureRecognizer` must not be self. 

- shouldRequireFailureOfGestureRecognizer: 

`otherGestureRecognizer == self` is always NO, for the same reason described above. 

### new implementation: 

After digging into various PRs, the idea seems to be that we want to have a precedence of "Forwarding recognizer >  Delaying recognizer > Other recognizers in platform view". 

- shouldBeRequiredToFailByGestureRecognizer:

`return otherGestureRecognizer != _forwardingRecognizer` means Delaying recognizer needs to be higher precedence than all non-Forwarding recognizer. (aka "Delaying recognizer > Other recognizers in platform view")

- shouldRequireFailureOfGestureRecognizer:

`return otherGestureRecognizer == _forwardingRecognizer` means Delaying recognizer needs to have lower precedence than forwarding recognizer. (aka "Forwarding recognizer > Delaying recognizer"). 

## Some research

This is a tricky one since pencil and finger triggers exactly the same callbacks. It turns out that when pencil is involved after finger interaction, the platform view's "forwarding" gesture recognizer is stuck at failed state. This seems to be an iOS bug, because according to [the API doc](https://developer.apple.com/documentation/uikit/uigesturerecognizerstate/uigesturerecognizerstatefailed?language=objc), it should be reset back to "possible" state:

> No action message is sent and the gesture recognizer is reset to [UIGestureRecognizerStatePossible](https://developer.apple.com/documentation/uikit/uigesturerecognizerstate/uigesturerecognizerstatepossible?language=objc).

However, when iPad pencil is involved, the state is not reset. I tried to KVO the state property, and wasn't able to capture the change. This means the state change very likely happened internally within the recognizer via the backing ivar of the state property.

*List which issues are fixed by this PR. You must list at least one issue.*

Fixes flutter#136244

*If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
nick9822 pushed a commit to nick9822/flutter that referenced this pull request Dec 18, 2024
flutter/engine#55889)

�h iPad pencil getting stuck (flutter#55724)"

This reverts commit 21eb449.

Revert since a customer reported the platform view touch is broken after the change. 

*List which issues are fixed by this PR. You must list at least one issue.*
Reopens flutter/engine#55724

*If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
autosubmit Merge PR when tree becomes green via auto submit App platform-ios test: all See https://github.com/flutter/engine/blob/main/docs/ci/Engine-pre-submits-and-post-submits.md
Projects
None yet
2 participants