Fix iOS platform view's mask view blocking touch events. (flutter/engine#21286)

This commit is contained in:
Chris Yang 2020-09-21 15:22:47 -07:00 committed by GitHub
parent 0b0fe27af8
commit 73ae0bb94c
4 changed files with 87 additions and 6 deletions

View File

@ -90,6 +90,15 @@ void ResetAnchor(CALayer* layer) {
return self;
}
// In some scenarios, when we add this view as a maskView of the ChildClippingView, iOS added
// this view as a subview of the ChildClippingView.
// This results this view blocking touch events on the ChildClippingView.
// So we should always ignore any touch events sent to this view.
// See https://github.com/flutter/flutter/issues/66044
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
return NO;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);

View File

@ -23,7 +23,9 @@
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--maskview-blocking"]) {
self.window.tintColor = UIColor.systemPinkColor;
}
NSDictionary<NSString*, NSString*>* launchArgsMap = @{
// The Platform view golden test args should match `PlatformViewGoldenTestManager`.
@"--locale-initialization" : @"locale_initialization",
@ -58,7 +60,6 @@
*stop = YES;
}
}];
if (flutterViewControllerTestName) {
[self setupFlutterViewControllerTest:flutterViewControllerTestName];
} else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--screen-before-flutter"]) {

View File

@ -110,9 +110,54 @@ static const NSInteger kSecondsToWaitForPlatformView = 30;
[[XCTNSPredicateExpectation alloc] initWithPredicate:predicate object:platformView];
[platformView tap];
[self waitForExpectations:@[ expection ] timeout:kSecondsToWaitForPlatformView];
XCTAssertEqualObjects(platformView.label,
@"-gestureTouchesBegan-gestureTouchesEnded-platformViewTapped");
}
- (void)testGestureWithMaskViewBlockingPlatformView {
XCUIApplication* app = [[XCUIApplication alloc] init];
app.launchArguments = @[ @"--gesture-accept", @"--maskview-blocking" ];
[app launch];
NSPredicate* predicateToFindPlatformView =
[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject,
NSDictionary<NSString*, id>* _Nullable bindings) {
XCUIElement* element = evaluatedObject;
return [element.identifier hasPrefix:@"platform_view"];
}];
XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView];
if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) {
NSLog(@"%@", app.debugDescription);
XCTFail(@"Failed due to not able to find any platformView with %@ seconds",
@(kSecondsToWaitForPlatformView));
}
XCTAssertNotNil(platformView);
XCTAssertEqualObjects(platformView.label, @"");
NSPredicate* predicate = [NSPredicate
predicateWithFormat:@"label == %@",
@"-gestureTouchesBegan-gestureTouchesEnded-platformViewTapped"];
XCTNSPredicateExpectation* expection =
[[XCTNSPredicateExpectation alloc] initWithPredicate:predicate object:platformView];
XCUICoordinate* coordinate =
[self getNormalizedCoordinate:app
point:CGVectorMake(platformView.frame.origin.x + 10,
platformView.frame.origin.y + 10)];
[coordinate tap];
[self waitForExpectations:@[ expection ] timeout:kSecondsToWaitForPlatformView];
XCTAssertEqualObjects(platformView.label,
@"-gestureTouchesBegan-gestureTouchesEnded-platformViewTapped");
}
- (XCUICoordinate*)getNormalizedCoordinate:(XCUIApplication*)app point:(CGVector)vector {
XCUICoordinate* appZero = [app coordinateWithNormalizedOffset:CGVectorMake(0, 0)];
XCUICoordinate* coordinate = [appZero coordinateWithOffset:vector];
return coordinate;
}
@end

View File

@ -336,9 +336,9 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BaseP
MultiPlatformViewBackgroundForegroundScenario(Window window, {this.firstId, this.secondId})
: assert(window != null),
super(window) {
_nextFrame = _firstFrame;
createPlatformView(window, 'platform view 1', firstId);
createPlatformView(window, 'platform view 2', secondId);
_nextFrame = _firstFrame;
}
/// The platform view identifier to use for the first platform view.
@ -532,6 +532,8 @@ class PlatformViewForTouchIOSScenario extends Scenario
int _viewId;
bool _accept;
VoidCallback _nextFrame;
/// Creates the PlatformView scenario.
///
/// The [window] parameter must not be null.
@ -545,14 +547,24 @@ class PlatformViewForTouchIOSScenario extends Scenario
} else {
createPlatformView(window, text, id);
}
_nextFrame = _firstFrame;
}
@override
void onBeginFrame(Duration duration) {
final SceneBuilder builder = SceneBuilder();
_nextFrame();
}
builder.pushOffset(0, 0);
finishBuilderByAddingPlatformViewAndPicture(builder, _viewId);
@override
void onDrawFrame() {
// Some iOS gesture recognizers bugs are introduced in the second frame (with a different platform view rect) after laying out the platform view.
// So in this test, we load 2 frames to ensure that we cover those cases.
// See https://github.com/flutter/flutter/issues/66044
if (_nextFrame == _firstFrame) {
_nextFrame = _secondFrame;
window.scheduleFrame();
}
super.onDrawFrame();
}
@override
@ -585,6 +597,20 @@ class PlatformViewForTouchIOSScenario extends Scenario
}
}
void _firstFrame() {
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(0, 0);
finishBuilderByAddingPlatformViewAndPicture(builder, _viewId);
}
void _secondFrame() {
final SceneBuilder builder = SceneBuilder();
builder.pushOffset(5, 5);
finishBuilderByAddingPlatformViewAndPicture(builder, _viewId);
}
}
mixin _BasePlatformViewScenarioMixin on Scenario {