mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Always set change type to cancel with touchesCancelled on iOS platform view (#24333)
This commit is contained in:
parent
7e48c42679
commit
b9ecd8aca6
@ -15,6 +15,7 @@
|
||||
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
|
||||
#import "flutter/shell/platform/darwin/ios/ios_surface.h"
|
||||
#import "flutter/shell/platform/darwin/ios/ios_surface_gl.h"
|
||||
|
||||
@ -938,7 +939,12 @@ void FlutterPlatformViewsController::ResetFrameState() {
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
[_flutterViewController.get() touchesCancelled:touches withEvent:event];
|
||||
// In the event of platform view is removed, iOS generates a "stationary" change type instead of
|
||||
// "cancelled" change type.
|
||||
// Flutter needs all the cancelled touches to be "cancelled" change types in order to correctly
|
||||
// handle gesture sequence.
|
||||
// We always override the change type to "cancelled".
|
||||
[((FlutterViewController*)_flutterViewController.get()) forceTouchesCancelled:touches];
|
||||
_currentTouchPointersCount -= touches.count;
|
||||
if (_currentTouchPointersCount == 0) {
|
||||
self.state = UIGestureRecognizerStateFailed;
|
||||
|
||||
@ -626,7 +626,7 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(std::string name) {
|
||||
// Before setting flutter view controller, events are not dispatched.
|
||||
NSSet* touches1 = [[[NSSet alloc] init] autorelease];
|
||||
id event1 = OCMClassMock([UIEvent class]);
|
||||
id mockFlutterViewContoller = OCMClassMock([UIViewController class]);
|
||||
id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]);
|
||||
[forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
|
||||
OCMReject([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]);
|
||||
|
||||
@ -684,7 +684,7 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(std::string name) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
id mockFlutterViewContoller = OCMClassMock([UIViewController class]);
|
||||
id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]);
|
||||
{
|
||||
// ***** Sequence 1, finishing touch event with touchEnded ***** //
|
||||
flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
|
||||
@ -739,7 +739,7 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(std::string name) {
|
||||
NSSet* touches3 = [[[NSSet alloc] init] autorelease];
|
||||
id event3 = OCMClassMock([UIEvent class]);
|
||||
[forwardGectureRecognizer touchesCancelled:touches3 withEvent:event3];
|
||||
OCMVerify([mockFlutterViewContoller touchesCancelled:touches3 withEvent:event3]);
|
||||
OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches3]);
|
||||
|
||||
// Now the 2nd touch sequence should not be allowed.
|
||||
NSSet* touches4 = [[[NSSet alloc] init] autorelease];
|
||||
@ -803,7 +803,7 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(std::string name) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
id mockFlutterViewContoller = OCMClassMock([UIViewController class]);
|
||||
id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]);
|
||||
|
||||
flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
|
||||
|
||||
@ -866,6 +866,66 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(std::string name) {
|
||||
flutterPlatformViewsController->Reset();
|
||||
}
|
||||
|
||||
- (void)testFlutterPlatformViewTouchesCancelledEventAreForcedToBeCancelled {
|
||||
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
|
||||
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
|
||||
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
|
||||
/*platform=*/thread_task_runner,
|
||||
/*raster=*/thread_task_runner,
|
||||
/*ui=*/thread_task_runner,
|
||||
/*io=*/thread_task_runner);
|
||||
auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
|
||||
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
|
||||
/*delegate=*/mock_delegate,
|
||||
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
|
||||
/*platform_views_controller=*/flutterPlatformViewsController,
|
||||
/*task_runners=*/runners);
|
||||
|
||||
FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
|
||||
[[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease];
|
||||
flutterPlatformViewsController->RegisterViewFactory(
|
||||
factory, @"MockFlutterPlatformView",
|
||||
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
|
||||
FlutterResult result = ^(id result) {
|
||||
};
|
||||
flutterPlatformViewsController->OnMethodCall(
|
||||
[FlutterMethodCall
|
||||
methodCallWithMethodName:@"create"
|
||||
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
|
||||
result);
|
||||
|
||||
XCTAssertNotNil(gMockPlatformView);
|
||||
|
||||
// Find touch inteceptor view
|
||||
UIView* touchInteceptorView = gMockPlatformView;
|
||||
while (touchInteceptorView != nil &&
|
||||
![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
|
||||
touchInteceptorView = touchInteceptorView.superview;
|
||||
}
|
||||
XCTAssertNotNil(touchInteceptorView);
|
||||
|
||||
// Find ForwardGestureRecognizer
|
||||
UIGestureRecognizer* forwardGectureRecognizer = nil;
|
||||
for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
|
||||
if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
|
||||
forwardGectureRecognizer = gestureRecognizer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
id mockFlutterViewContoller = OCMClassMock([FlutterViewController class]);
|
||||
|
||||
flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
|
||||
|
||||
NSSet* touches1 = [NSSet setWithObject:@1];
|
||||
id event1 = OCMClassMock([UIEvent class]);
|
||||
[forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
|
||||
|
||||
[forwardGectureRecognizer touchesCancelled:touches1 withEvent:event1];
|
||||
OCMVerify([mockFlutterViewContoller forceTouchesCancelled:touches1]);
|
||||
|
||||
flutterPlatformViewsController->Reset();
|
||||
}
|
||||
|
||||
- (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashing {
|
||||
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
|
||||
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
|
||||
|
||||
@ -947,6 +947,11 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
[self dispatchTouches:touches pointerDataChangeOverride:nullptr];
|
||||
}
|
||||
|
||||
- (void)forceTouchesCancelled:(NSSet*)touches {
|
||||
flutter::PointerData::Change cancel = flutter::PointerData::Change::kCancel;
|
||||
[self dispatchTouches:touches pointerDataChangeOverride:&cancel];
|
||||
}
|
||||
|
||||
#pragma mark - Handle view resizing
|
||||
|
||||
- (void)updateViewportMetrics {
|
||||
|
||||
@ -29,6 +29,9 @@ extern NSNotificationName const FlutterViewControllerShowHomeIndicator;
|
||||
- (fml::WeakPtr<FlutterViewController>)getWeakPtr;
|
||||
- (std::shared_ptr<flutter::FlutterPlatformViewsController>&)platformViewsController;
|
||||
- (FlutterRestorationPlugin*)restorationPlugin;
|
||||
// Send touches to the Flutter Engine while forcing the change type to be cancelled.
|
||||
// The `phase`s in `touches` are ignored.
|
||||
- (void)forceTouchesCancelled:(NSSet*)touches;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user