@@ -61,6 +61,7 @@ @interface FlutterSecureTextInputView : FlutterTextInputView
61
61
62
62
@interface FlutterTextInputPlugin ()
63
63
@property (nonatomic , assign ) FlutterTextInputView* activeView;
64
+ @property (nonatomic , readonly ) UIView* inputHider;
64
65
@property (nonatomic , readonly ) UIView* keyboardViewContainer;
65
66
@property (nonatomic , readonly ) UIView* keyboardView;
66
67
@property (nonatomic , assign ) UIView* cachedFirstResponder;
@@ -422,6 +423,72 @@ - (void)testAutocorrectionPromptRectDoesNotAppearDuringScribble {
422
423
}
423
424
}
424
425
426
+ - (void )testInputHiderOverlapWithTextWhenScribbleIsDisabledAfterIOS17AndDoesNotOverlapBeforeIOS17 {
427
+ FlutterTextInputPlugin* myInputPlugin =
428
+ [[FlutterTextInputPlugin alloc ] initWithDelegate: OCMClassMock ([FlutterEngine class ])];
429
+
430
+ FlutterMethodCall* setClientCall =
431
+ [FlutterMethodCall methodCallWithMethodName: @" TextInput.setClient"
432
+ arguments: @[ @(123 ), self .mutableTemplateCopy ]];
433
+ [myInputPlugin handleMethodCall: setClientCall
434
+ result: ^(id _Nullable result){
435
+ }];
436
+
437
+ FlutterTextInputView* mockInputView = OCMPartialMock (myInputPlugin.activeView );
438
+ OCMStub ([mockInputView isScribbleAvailable ]).andReturn (NO );
439
+
440
+ // yOffset = 200.
441
+ NSArray * yOffsetMatrix = @[ @1 , @0 , @0 , @0 , @0 , @1 , @0 , @0 , @0 , @0 , @1 , @0 , @0 , @200 , @0 , @1 ];
442
+
443
+ FlutterMethodCall* setPlatformViewClientCall =
444
+ [FlutterMethodCall methodCallWithMethodName: @" TextInput.setEditableSizeAndTransform"
445
+ arguments: @{@" transform" : yOffsetMatrix}];
446
+ [myInputPlugin handleMethodCall: setPlatformViewClientCall
447
+ result: ^(id _Nullable result){
448
+ }];
449
+
450
+ if (@available (iOS 17 , *)) {
451
+ XCTAssert (CGRectEqualToRect (myInputPlugin.inputHider .frame , CGRectMake (0 , 200 , 0 , 0 )),
452
+ @" The input hider should overlap with the text on and after iOS 17" );
453
+
454
+ } else {
455
+ XCTAssert (CGRectEqualToRect (myInputPlugin.inputHider .frame , CGRectZero),
456
+ @" The input hider should be on the origin of screen on and before iOS 16." );
457
+ }
458
+ }
459
+
460
+ - (void )testSetSelectionRectsNotifiesTextChangeAfterIOS17AndDoesNotNotifyBeforeIOS17 {
461
+ FlutterTextInputPlugin* myInputPlugin =
462
+ [[FlutterTextInputPlugin alloc ] initWithDelegate: OCMClassMock ([FlutterEngine class ])];
463
+
464
+ FlutterMethodCall* setClientCall =
465
+ [FlutterMethodCall methodCallWithMethodName: @" TextInput.setClient"
466
+ arguments: @[ @(123 ), self .mutableTemplateCopy ]];
467
+ [myInputPlugin handleMethodCall: setClientCall
468
+ result: ^(id _Nullable result){
469
+ }];
470
+
471
+ id mockInputDelegate = OCMProtocolMock (@protocol (UITextInputDelegate));
472
+ myInputPlugin.activeView .inputDelegate = mockInputDelegate;
473
+
474
+ NSArray <NSNumber *>* selectionRect = [NSArray arrayWithObjects: @0 , @0 , @100 , @100 , @0 , @1 , nil ];
475
+ NSArray * selectionRects = [NSArray arrayWithObjects: selectionRect, nil ];
476
+ FlutterMethodCall* methodCall =
477
+ [FlutterMethodCall methodCallWithMethodName: @" Scribble.setSelectionRects"
478
+ arguments: selectionRects];
479
+ [myInputPlugin handleMethodCall: methodCall
480
+ result: ^(id _Nullable result){
481
+ }];
482
+
483
+ if (@available (iOS 17.0 , *)) {
484
+ OCMVerify ([mockInputDelegate textWillChange: myInputPlugin.activeView]);
485
+ OCMVerify ([mockInputDelegate textDidChange: myInputPlugin.activeView]);
486
+ } else {
487
+ OCMVerify (never (), [mockInputDelegate textWillChange: myInputPlugin.activeView]);
488
+ OCMVerify (never (), [mockInputDelegate textDidChange: myInputPlugin.activeView]);
489
+ }
490
+ }
491
+
425
492
- (void )testTextRangeFromPositionMatchesUITextViewBehavior {
426
493
FlutterTextInputView* inputView = [[FlutterTextInputView alloc ] initWithOwner: textInputPlugin];
427
494
FlutterTextPosition* fromPosition = [FlutterTextPosition positionWithIndex: 2 ];
0 commit comments