[ios]enable the webview non tappable workaround by checking subviews recursively (flutter/engine#57168)
The original workaround ([PR](https://github.com/flutter/engine/pull/56804)) works for the official web view plugin, but it doesn't work for a third party plugin `flutter_inappwebview` ([issue](https://github.com/pichillilorenzo/flutter_inappwebview)). Upon discussion with the author of that plugin, it turns out that their platform view is not a WKWebView, but rather a wrapper of WKWebView. This PR performs a DFS search of the view hierarchy, and enable the workaround as long as there's a WKWebView inside. TODO: pending sample project: I am quite positive that it should work, but **I haven't tried it since I don't have a sample project yet**. I have requested a sample project with them so I can verify the solution. *List which issues are fixed by this PR. You must list at least one issue.* https://github.com/pichillilorenzo/flutter_inappwebview/issues/2415 *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
This commit is contained in:
parent
3d8fc3c652
commit
f9125dfd91
@ -566,6 +566,18 @@ static BOOL _preparedOnce = NO;
|
||||
self.delayingRecognizer.state = UIGestureRecognizerStateFailed;
|
||||
}
|
||||
|
||||
- (BOOL)containsWebView:(UIView*)view {
|
||||
if ([view isKindOfClass:[WKWebView class]]) {
|
||||
return YES;
|
||||
}
|
||||
for (UIView* subview in view.subviews) {
|
||||
if ([self containsWebView:subview]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)blockGesture {
|
||||
switch (_blockingPolicy) {
|
||||
case FlutterPlatformViewGestureRecognizersBlockingPolicyEager:
|
||||
@ -581,7 +593,7 @@ static BOOL _preparedOnce = NO;
|
||||
// FlutterPlatformViewGestureRecognizersBlockingPolicyEager, but we should try it if a similar
|
||||
// issue arises for the other policy.
|
||||
if (@available(iOS 18.2, *)) {
|
||||
if ([self.embeddedView isKindOfClass:[WKWebView class]]) {
|
||||
if ([self containsWebView:self.embeddedView]) {
|
||||
[self removeGestureRecognizer:self.delayingRecognizer];
|
||||
[self addGestureRecognizer:self.delayingRecognizer];
|
||||
}
|
||||
|
@ -140,6 +140,46 @@ const float kFloatCompareEpsilon = 0.001;
|
||||
|
||||
@end
|
||||
|
||||
@interface FlutterPlatformViewsTestMockWrapperWebView : NSObject <FlutterPlatformView>
|
||||
@property(nonatomic, strong) UIView* view;
|
||||
@property(nonatomic, assign) BOOL viewCreated;
|
||||
@end
|
||||
|
||||
@implementation FlutterPlatformViewsTestMockWrapperWebView
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_view = [[UIView alloc] init];
|
||||
[_view addSubview:[[WKWebView alloc] init]];
|
||||
gMockPlatformView = _view;
|
||||
_viewCreated = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (UIView*)view {
|
||||
[self checkViewCreatedOnce];
|
||||
return _view;
|
||||
}
|
||||
|
||||
- (void)checkViewCreatedOnce {
|
||||
if (self.viewCreated) {
|
||||
abort();
|
||||
}
|
||||
self.viewCreated = YES;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface FlutterPlatformViewsTestMockWrapperWebViewFactory : NSObject <FlutterPlatformViewFactory>
|
||||
@end
|
||||
|
||||
@implementation FlutterPlatformViewsTestMockWrapperWebViewFactory
|
||||
- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
|
||||
viewIdentifier:(int64_t)viewId
|
||||
arguments:(id _Nullable)args {
|
||||
return [[FlutterPlatformViewsTestMockWrapperWebView alloc] init];
|
||||
}
|
||||
@end
|
||||
|
||||
namespace flutter {
|
||||
namespace {
|
||||
class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::Delegate {
|
||||
@ -3153,6 +3193,71 @@ fml::RefPtr<fml::TaskRunner> GetDefaultTaskRunner() {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)
|
||||
testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldRemoveAndAddBackDelayingRecognizerForWrapperWebView {
|
||||
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
|
||||
|
||||
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
|
||||
/*platform=*/GetDefaultTaskRunner(),
|
||||
/*raster=*/GetDefaultTaskRunner(),
|
||||
/*ui=*/GetDefaultTaskRunner(),
|
||||
/*io=*/GetDefaultTaskRunner());
|
||||
FlutterPlatformViewsController* flutterPlatformViewsController =
|
||||
[[FlutterPlatformViewsController alloc] init];
|
||||
flutterPlatformViewsController.taskRunner = GetDefaultTaskRunner();
|
||||
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
|
||||
/*delegate=*/mock_delegate,
|
||||
/*rendering_api=*/mock_delegate.settings_.enable_impeller
|
||||
? flutter::IOSRenderingAPI::kMetal
|
||||
: flutter::IOSRenderingAPI::kSoftware,
|
||||
/*platform_views_controller=*/flutterPlatformViewsController,
|
||||
/*task_runners=*/runners,
|
||||
/*worker_task_runner=*/nil,
|
||||
/*is_gpu_disabled_jsync_switch=*/std::make_shared<fml::SyncSwitch>());
|
||||
|
||||
FlutterPlatformViewsTestMockWrapperWebViewFactory* factory =
|
||||
[[FlutterPlatformViewsTestMockWrapperWebViewFactory alloc] init];
|
||||
[flutterPlatformViewsController
|
||||
registerViewFactory:factory
|
||||
withId:@"MockWrapperWebView"
|
||||
gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager];
|
||||
FlutterResult result = ^(id result) {
|
||||
};
|
||||
[flutterPlatformViewsController
|
||||
onMethodCall:[FlutterMethodCall
|
||||
methodCallWithMethodName:@"create"
|
||||
arguments:@{@"id" : @2, @"viewType" : @"MockWrapperWebView"}]
|
||||
result:result];
|
||||
|
||||
XCTAssertNotNil(gMockPlatformView);
|
||||
|
||||
// Find touch inteceptor view
|
||||
UIView* touchInteceptorView = gMockPlatformView;
|
||||
while (touchInteceptorView != nil &&
|
||||
![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
|
||||
touchInteceptorView = touchInteceptorView.superview;
|
||||
}
|
||||
XCTAssertNotNil(touchInteceptorView);
|
||||
|
||||
XCTAssert(touchInteceptorView.gestureRecognizers.count == 2);
|
||||
UIGestureRecognizer* delayingRecognizer = touchInteceptorView.gestureRecognizers[0];
|
||||
UIGestureRecognizer* forwardingRecognizer = touchInteceptorView.gestureRecognizers[1];
|
||||
|
||||
XCTAssert([delayingRecognizer isKindOfClass:[FlutterDelayingGestureRecognizer class]]);
|
||||
XCTAssert([forwardingRecognizer isKindOfClass:[ForwardingGestureRecognizer class]]);
|
||||
|
||||
[(FlutterTouchInterceptingView*)touchInteceptorView blockGesture];
|
||||
|
||||
if (@available(iOS 18.2, *)) {
|
||||
// Since we remove and add back delayingRecognizer, it would be reordered to the last.
|
||||
XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], forwardingRecognizer);
|
||||
XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], delayingRecognizer);
|
||||
} else {
|
||||
XCTAssertEqual(touchInteceptorView.gestureRecognizers[0], delayingRecognizer);
|
||||
XCTAssertEqual(touchInteceptorView.gestureRecognizers[1], forwardingRecognizer);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)
|
||||
testFlutterPlatformViewBlockGestureUnderEagerPolicyShouldNotRemoveAndAddBackDelayingRecognizerForNonWebView {
|
||||
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
|
||||
|
Loading…
x
Reference in New Issue
Block a user