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

Commit a0a1a1d

Browse files
authored
[ios]fix ios 16 auto correction highlight showing on top left corner (#47279)
This PR hides the system highlights in iOS 16 (except when scribble is enabled). Note that auto correction highlight is still drawn by flutter, so it still works. I don't think we need to CP this, since it's iOS 16 only, and iOS 17 is already out. ## Why not use system highlight? Unlike iOS 17, the iOS 16 system highlight only respect the width provided by `firstRectForRange`, but not the height. I have audited all sizing related APIs in UITextInput (doc [here](https://developer.apple.com/documentation/uikit/uitextinput#1653155)), specifically, `firstRect(for:)`, `caretRect(for:)` and `selectionRects(for:)`, and they all return the correct height. ## About scribble The initial implementation of `firstRectForRange` (that returns the first selection rect) was introduced for the scribble feature on iPad (code [here](1ae721c#diff-4c7b102c0690b8ec5e2212b079f5d69fe3f816c84e47ce94bc7bc89312f39e40R1487-R1505)). It turns out that a non-zero rect is required for scribble's advanced feature to work (e.g. inserting a space with a vertical bar). So we can't apply this fix for scribble. *List which issues are fixed by this PR. You must list at least one issue.* Fixes flutter/flutter#136802 *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
1 parent 298b137 commit a0a1a1d

File tree

2 files changed

+69
-28
lines changed

2 files changed

+69
-28
lines changed

shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,6 +1690,16 @@ - (CGRect)firstRectForRange:(UITextRange*)range {
16901690
}
16911691
}
16921692

1693+
// The iOS 16 system highlight does not repect the height returned by `firstRectForRange`
1694+
// API (unlike iOS 17). So we return CGRectZero to hide it (unless if scribble is enabled).
1695+
// To support scribble's advanced gestures (e.g. insert a space with a vertical bar),
1696+
// at least 1 character's width is required.
1697+
if (@available(iOS 17, *)) {
1698+
// No-op
1699+
} else if (![self isScribbleAvailable]) {
1700+
return CGRectZero;
1701+
}
1702+
16931703
NSUInteger first = start;
16941704
if (end < start) {
16951705
first = end;

shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.mm

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,19 +1558,19 @@ - (void)testUpdateFirstRectForRange {
15581558
[inputView firstRectForRange:range]));
15591559
}
15601560

1561-
- (void)testFirstRectForRangeReturnsCorrectRectOnASingleLineLeftToRight {
1561+
- (void)testFirstRectForRangeReturnsNoneZeroRectWhenScribbleIsEnabled {
15621562
FlutterTextInputView* inputView = [[FlutterTextInputView alloc] initWithOwner:textInputPlugin];
15631563
[inputView setTextInputState:@{@"text" : @"COMPOSING"}];
15641564

1565+
FlutterTextInputView* mockInputView = OCMPartialMock(inputView);
1566+
OCMStub([mockInputView isScribbleAvailable]).andReturn(YES);
1567+
15651568
[inputView setSelectionRects:@[
15661569
[FlutterTextSelectionRect selectionRectWithRect:CGRectMake(0, 0, 100, 100) position:0U],
15671570
[FlutterTextSelectionRect selectionRectWithRect:CGRectMake(100, 0, 100, 100) position:1U],
15681571
[FlutterTextSelectionRect selectionRectWithRect:CGRectMake(200, 0, 100, 100) position:2U],
15691572
[FlutterTextSelectionRect selectionRectWithRect:CGRectMake(300, 0, 100, 100) position:3U],
15701573
]];
1571-
FlutterTextRange* singleRectRange = [FlutterTextRange rangeWithNSRange:NSMakeRange(1, 1)];
1572-
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, 0, 100, 100),
1573-
[inputView firstRectForRange:singleRectRange]));
15741574

15751575
FlutterTextRange* multiRectRange = [FlutterTextRange rangeWithNSRange:NSMakeRange(1, 3)];
15761576

@@ -1581,6 +1581,34 @@ - (void)testFirstRectForRangeReturnsCorrectRectOnASingleLineLeftToRight {
15811581
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, 0, 100, 100),
15821582
[inputView firstRectForRange:multiRectRange]));
15831583
}
1584+
}
1585+
1586+
- (void)testFirstRectForRangeReturnsCorrectRectOnASingleLineLeftToRight {
1587+
FlutterTextInputView* inputView = [[FlutterTextInputView alloc] initWithOwner:textInputPlugin];
1588+
[inputView setTextInputState:@{@"text" : @"COMPOSING"}];
1589+
1590+
[inputView setSelectionRects:@[
1591+
[FlutterTextSelectionRect selectionRectWithRect:CGRectMake(0, 0, 100, 100) position:0U],
1592+
[FlutterTextSelectionRect selectionRectWithRect:CGRectMake(100, 0, 100, 100) position:1U],
1593+
[FlutterTextSelectionRect selectionRectWithRect:CGRectMake(200, 0, 100, 100) position:2U],
1594+
[FlutterTextSelectionRect selectionRectWithRect:CGRectMake(300, 0, 100, 100) position:3U],
1595+
]];
1596+
FlutterTextRange* singleRectRange = [FlutterTextRange rangeWithNSRange:NSMakeRange(1, 1)];
1597+
if (@available(iOS 17, *)) {
1598+
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, 0, 100, 100),
1599+
[inputView firstRectForRange:singleRectRange]));
1600+
} else {
1601+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:singleRectRange]));
1602+
}
1603+
1604+
FlutterTextRange* multiRectRange = [FlutterTextRange rangeWithNSRange:NSMakeRange(1, 3)];
1605+
1606+
if (@available(iOS 17, *)) {
1607+
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, 0, 300, 100),
1608+
[inputView firstRectForRange:multiRectRange]));
1609+
} else {
1610+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:multiRectRange]));
1611+
}
15841612

15851613
[inputView setTextInputState:@{@"text" : @"COM"}];
15861614
FlutterTextRange* rangeOutsideBounds = [FlutterTextRange rangeWithNSRange:NSMakeRange(3, 1)];
@@ -1598,16 +1626,19 @@ - (void)testFirstRectForRangeReturnsCorrectRectOnASingleLineRightToLeft {
15981626
[FlutterTextSelectionRect selectionRectWithRect:CGRectMake(0, 0, 100, 100) position:3U],
15991627
]];
16001628
FlutterTextRange* singleRectRange = [FlutterTextRange rangeWithNSRange:NSMakeRange(1, 1)];
1601-
XCTAssertTrue(CGRectEqualToRect(CGRectMake(200, 0, 100, 100),
1602-
[inputView firstRectForRange:singleRectRange]));
1629+
if (@available(iOS 17, *)) {
1630+
XCTAssertTrue(CGRectEqualToRect(CGRectMake(200, 0, 100, 100),
1631+
[inputView firstRectForRange:singleRectRange]));
1632+
} else {
1633+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:singleRectRange]));
1634+
}
16031635

16041636
FlutterTextRange* multiRectRange = [FlutterTextRange rangeWithNSRange:NSMakeRange(1, 3)];
16051637
if (@available(iOS 17, *)) {
16061638
XCTAssertTrue(CGRectEqualToRect(CGRectMake(0, 0, 300, 100),
16071639
[inputView firstRectForRange:multiRectRange]));
16081640
} else {
1609-
XCTAssertTrue(CGRectEqualToRect(CGRectMake(200, 0, 100, 100),
1610-
[inputView firstRectForRange:multiRectRange]));
1641+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:multiRectRange]));
16111642
}
16121643

16131644
[inputView setTextInputState:@{@"text" : @"COM"}];
@@ -1630,17 +1661,20 @@ - (void)testFirstRectForRangeReturnsCorrectRectOnMultipleLinesLeftToRight {
16301661
[FlutterTextSelectionRect selectionRectWithRect:CGRectMake(300, 100, 100, 100) position:7U],
16311662
]];
16321663
FlutterTextRange* singleRectRange = [FlutterTextRange rangeWithNSRange:NSMakeRange(1, 1)];
1633-
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, 0, 100, 100),
1634-
[inputView firstRectForRange:singleRectRange]));
1664+
if (@available(iOS 17, *)) {
1665+
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, 0, 100, 100),
1666+
[inputView firstRectForRange:singleRectRange]));
1667+
} else {
1668+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:singleRectRange]));
1669+
}
16351670

16361671
FlutterTextRange* multiRectRange = [FlutterTextRange rangeWithNSRange:NSMakeRange(1, 4)];
16371672

16381673
if (@available(iOS 17, *)) {
16391674
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, 0, 300, 100),
16401675
[inputView firstRectForRange:multiRectRange]));
16411676
} else {
1642-
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, 0, 100, 100),
1643-
[inputView firstRectForRange:multiRectRange]));
1677+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:multiRectRange]));
16441678
}
16451679
}
16461680

@@ -1659,16 +1693,19 @@ - (void)testFirstRectForRangeReturnsCorrectRectOnMultipleLinesRightToLeft {
16591693
[FlutterTextSelectionRect selectionRectWithRect:CGRectMake(0, 100, 100, 100) position:7U],
16601694
]];
16611695
FlutterTextRange* singleRectRange = [FlutterTextRange rangeWithNSRange:NSMakeRange(1, 1)];
1662-
XCTAssertTrue(CGRectEqualToRect(CGRectMake(200, 0, 100, 100),
1663-
[inputView firstRectForRange:singleRectRange]));
1696+
if (@available(iOS 17, *)) {
1697+
XCTAssertTrue(CGRectEqualToRect(CGRectMake(200, 0, 100, 100),
1698+
[inputView firstRectForRange:singleRectRange]));
1699+
} else {
1700+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:singleRectRange]));
1701+
}
16641702

16651703
FlutterTextRange* multiRectRange = [FlutterTextRange rangeWithNSRange:NSMakeRange(1, 4)];
16661704
if (@available(iOS 17, *)) {
16671705
XCTAssertTrue(CGRectEqualToRect(CGRectMake(0, 0, 300, 100),
16681706
[inputView firstRectForRange:multiRectRange]));
16691707
} else {
1670-
XCTAssertTrue(CGRectEqualToRect(CGRectMake(200, 0, 100, 100),
1671-
[inputView firstRectForRange:multiRectRange]));
1708+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:multiRectRange]));
16721709
}
16731710
}
16741711

@@ -1691,8 +1728,7 @@ - (void)testFirstRectForRangeReturnsCorrectRectOnSingleLineWithVaryingMinYAndMax
16911728
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, -10, 300, 120),
16921729
[inputView firstRectForRange:multiRectRange]));
16931730
} else {
1694-
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, 10, 100, 80),
1695-
[inputView firstRectForRange:multiRectRange]));
1731+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:multiRectRange]));
16961732
}
16971733
}
16981734

@@ -1715,8 +1751,7 @@ - (void)testFirstRectForRangeReturnsCorrectRectOnSingleLineWithVaryingMinYAndMax
17151751
XCTAssertTrue(CGRectEqualToRect(CGRectMake(0, -10, 300, 120),
17161752
[inputView firstRectForRange:multiRectRange]));
17171753
} else {
1718-
XCTAssertTrue(CGRectEqualToRect(CGRectMake(200, -10, 100, 120),
1719-
[inputView firstRectForRange:multiRectRange]));
1754+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:multiRectRange]));
17201755
}
17211756
}
17221757

@@ -1739,8 +1774,7 @@ - (void)testFirstRectForRangeReturnsCorrectRectWithOverlappingRectsExceedingThre
17391774
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, 0, 300, 100),
17401775
[inputView firstRectForRange:multiRectRange]));
17411776
} else {
1742-
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, 0, 100, 100),
1743-
[inputView firstRectForRange:multiRectRange]));
1777+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:multiRectRange]));
17441778
}
17451779
}
17461780

@@ -1763,8 +1797,7 @@ - (void)testFirstRectForRangeReturnsCorrectRectWithOverlappingRectsExceedingThre
17631797
XCTAssertTrue(CGRectEqualToRect(CGRectMake(0, 0, 300, 100),
17641798
[inputView firstRectForRange:multiRectRange]));
17651799
} else {
1766-
XCTAssertTrue(CGRectEqualToRect(CGRectMake(200, 0, 100, 100),
1767-
[inputView firstRectForRange:multiRectRange]));
1800+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:multiRectRange]));
17681801
}
17691802
}
17701803

@@ -1787,8 +1820,7 @@ - (void)testFirstRectForRangeReturnsCorrectRectWithOverlappingRectsWithinThresho
17871820
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, 0, 400, 140),
17881821
[inputView firstRectForRange:multiRectRange]));
17891822
} else {
1790-
XCTAssertTrue(CGRectEqualToRect(CGRectMake(100, 0, 100, 100),
1791-
[inputView firstRectForRange:multiRectRange]));
1823+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:multiRectRange]));
17921824
}
17931825
}
17941826

@@ -1811,8 +1843,7 @@ - (void)testFirstRectForRangeReturnsCorrectRectWithOverlappingRectsWithinThresho
18111843
XCTAssertTrue(CGRectEqualToRect(CGRectMake(0, 0, 400, 140),
18121844
[inputView firstRectForRange:multiRectRange]));
18131845
} else {
1814-
XCTAssertTrue(CGRectEqualToRect(CGRectMake(300, 0, 100, 100),
1815-
[inputView firstRectForRange:multiRectRange]));
1846+
XCTAssertTrue(CGRectEqualToRect(CGRectZero, [inputView firstRectForRange:multiRectRange]));
18161847
}
18171848
}
18181849

0 commit comments

Comments
 (0)