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

Commit 2d9e990

Browse files
committed
addressing comments
1 parent c74778c commit 2d9e990

File tree

4 files changed

+73
-27
lines changed

4 files changed

+73
-27
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@ FLUTTER_DARWIN_EXPORT
7171
@property(nonatomic, copy) UITextContentType textContentType API_AVAILABLE(ios(10.0));
7272

7373
@property(nonatomic, assign) id<FlutterTextInputDelegate> textInputDelegate;
74-
@property(class, nonatomic, weak, assign)
75-
UIAccessibilityElement* backingTextInputAccessibilityObject;
74+
@property(nonatomic, assign) UIAccessibilityElement* backingTextInputAccessibilityObject;
7675

7776
@end
7877
#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTPLUGIN_H_

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

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
static const char _kTextAffinityUpstream[] = "TextAffinity.upstream";
1515
// A delay before enabling the accessibility of FlutterTextInputView after
1616
// it is activated.
17-
static constexpr double _kUITextInputAccessibilityEnablingDelaySeconds = 0.5;
17+
static constexpr double kUITextInputAccessibilityEnablingDelaySeconds = 0.5;
1818

1919
// The "canonical" invalid CGRect, similar to CGRectNull, used to
2020
// indicate a CGRect involved in firstRectForRange calculation is
@@ -440,8 +440,6 @@ @implementation FlutterTextInputView {
440440
CGRect _cachedFirstRect;
441441
}
442442

443-
static UIAccessibilityElement* _backingTextInputAccessibilityObject;
444-
445443
@synthesize tokenizer = _tokenizer;
446444

447445
- (instancetype)init {
@@ -541,14 +539,6 @@ - (void)dealloc {
541539
[super dealloc];
542540
}
543541

544-
+ (UIAccessibilityElement*)backingTextInputAccessibilityObject {
545-
return _backingTextInputAccessibilityObject;
546-
}
547-
548-
+ (void)setBackingTextInputAccessibilityObject:(UIAccessibilityElement*)element {
549-
_backingTextInputAccessibilityObject = element;
550-
}
551-
552542
- (void)setTextInputClient:(int)client {
553543
_textInputClient = client;
554544
}
@@ -1122,15 +1112,18 @@ - (void)deleteBackward {
11221112
[self replaceRange:_selectedTextRange withText:@""];
11231113
}
11241114

1115+
- (void)postAccessibilityNotification:(UIAccessibilityNotifications)notification target:(id)target {
1116+
UIAccessibilityPostNotification(notification, target);
1117+
}
1118+
11251119
- (void)accessibilityElementDidBecomeFocused {
11261120
if ([self accessibilityElementIsFocused]) {
11271121
// For most of the cases, this flutter text input view should never
11281122
// receive the focus. If we do receive the focus, we make the best effort
11291123
// to send the focus back to the real text field.
1130-
__strong UIAccessibilityElement* textInput =
1131-
FlutterTextInputView.backingTextInputAccessibilityObject;
1132-
FML_DCHECK(textInput);
1133-
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, textInput);
1124+
FML_DCHECK(_backingTextInputAccessibilityObject);
1125+
[self postAccessibilityNotification:UIAccessibilityScreenChangedNotification
1126+
target:_backingTextInputAccessibilityObject];
11341127
}
11351128
}
11361129

@@ -1182,6 +1175,8 @@ @interface FlutterTextInputPlugin ()
11821175

11831176
@implementation FlutterTextInputPlugin
11841177

1178+
NSTimer* _enableFlutterTextInputViewAccessibilityTimer;
1179+
11851180
@synthesize textInputDelegate = _textInputDelegate;
11861181

11871182
- (instancetype)init {
@@ -1203,10 +1198,17 @@ - (void)dealloc {
12031198
[_reusableInputView release];
12041199
[_inputHider release];
12051200
[_autofillContext release];
1206-
1201+
[self removeEnableFlutterTextInputViewAccessibilityTimer];
12071202
[super dealloc];
12081203
}
12091204

1205+
- (void)removeEnableFlutterTextInputViewAccessibilityTimer {
1206+
if (_enableFlutterTextInputViewAccessibilityTimer) {
1207+
[_enableFlutterTextInputViewAccessibilityTimer invalidate];
1208+
_enableFlutterTextInputViewAccessibilityTimer = nil;
1209+
}
1210+
}
1211+
12101212
- (UIView<UITextInput>*)textInputView {
12111213
return _activeView;
12121214
}
@@ -1267,21 +1269,26 @@ - (void)showTextInput {
12671269
// with a semantics update sent to the engine. The voiceover will focus
12681270
// the newly attached active view while performing accessibility update.
12691271
// This results in accessibility focus stuck at the FlutterTextInputView.
1270-
[NSTimer scheduledTimerWithTimeInterval:_kUITextInputAccessibilityEnablingDelaySeconds
1271-
target:self
1272-
selector:@selector(enableActiveViewAccessibility:)
1273-
userInfo:nil
1274-
repeats:NO];
1272+
if (!_enableFlutterTextInputViewAccessibilityTimer) {
1273+
_enableFlutterTextInputViewAccessibilityTimer =
1274+
[NSTimer scheduledTimerWithTimeInterval:kUITextInputAccessibilityEnablingDelaySeconds
1275+
target:self
1276+
selector:@selector(enableActiveViewAccessibility:)
1277+
userInfo:nil
1278+
repeats:NO];
1279+
}
12751280
[_activeView becomeFirstResponder];
12761281
}
12771282

12781283
- (void)enableActiveViewAccessibility:(NSTimer*)time {
12791284
if (_activeView.isFirstResponder) {
12801285
_activeView.accessibilityEnabled = YES;
12811286
}
1287+
_enableFlutterTextInputViewAccessibilityTimer = nil;
12821288
}
12831289

12841290
- (void)hideTextInput {
1291+
[self removeEnableFlutterTextInputViewAccessibilityTimer];
12851292
_activeView.accessibilityEnabled = NO;
12861293
[_activeView resignFirstResponder];
12871294
[_activeView removeFromSuperview];

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,30 @@ - (void)setTextInputState:(NSDictionary*)state;
2020
- (void)setMarkedRect:(CGRect)markedRect;
2121
- (void)updateEditingState;
2222
- (BOOL)isVisibleToAutofill;
23+
24+
@end
25+
26+
@interface FlutterTextInputViewSpy : FlutterTextInputView
27+
@property(nonatomic, assign) UIAccessibilityNotifications receivedNotification;
28+
@property(nonatomic, assign) id receivedNotificationTarget;
29+
@property(nonatomic, assign) BOOL isAccessibilityFocused;
30+
31+
- (void)postAccessibilityNotification:(UIAccessibilityNotifications)notification target:(id)target;
32+
33+
@end
34+
35+
@implementation FlutterTextInputViewSpy {
36+
}
37+
38+
- (void)postAccessibilityNotification:(UIAccessibilityNotifications)notification target:(id)target {
39+
self.receivedNotification = notification;
40+
self.receivedNotificationTarget = target;
41+
}
42+
43+
- (BOOL)accessibilityElementIsFocused {
44+
return _isAccessibilityFocused;
45+
}
46+
2347
@end
2448

2549
@interface FlutterSecureTextInputView : FlutterTextInputView
@@ -779,4 +803,19 @@ - (void)testUITextInputAccessibilityNotHiddenWhenShowed {
779803
XCTAssertEqual([inputFields count], 0u);
780804
}
781805

806+
- (void)testFlutterTextInputViewDirectFocusToBackingTextInput {
807+
FlutterTextInputViewSpy* inputView = [[FlutterTextInputViewSpy alloc] init];
808+
inputView.textInputDelegate = engine;
809+
UIView* container = [[UIView alloc] init];
810+
UIAccessibilityElement* backing =
811+
[[UIAccessibilityElement alloc] initWithAccessibilityContainer:container];
812+
inputView.backingTextInputAccessibilityObject = backing;
813+
// Simulate accessibility focus.
814+
inputView.isAccessibilityFocused = YES;
815+
[inputView accessibilityElementDidBecomeFocused];
816+
817+
XCTAssertEqual(inputView.receivedNotification, UIAccessibilityScreenChangedNotification);
818+
XCTAssertEqual(inputView.receivedNotificationTarget, backing);
819+
}
820+
782821
@end

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,14 @@ - (void)dealloc {
198198
- (void)setSemanticsNode:(const flutter::SemanticsNode*)node {
199199
[super setSemanticsNode:node];
200200
_inactive_text_input.text = @(node->value.data());
201+
FlutterTextInputView* textInput = (FlutterTextInputView*)[self bridge]->textInputView();
201202
if ([self node].HasFlag(flutter::SemanticsFlags::kIsFocused)) {
203+
textInput.backingTextInputAccessibilityObject = self;
202204
// The text input view must have a non-trivial size for the accessibility
203205
// system to send text editing events.
204-
FlutterTextInputView.backingTextInputAccessibilityObject = self;
205-
[self bridge]->textInputView().frame = CGRectMake(0.0, 0.0, 1.0, 1.0);
206-
} else if (FlutterTextInputView.backingTextInputAccessibilityObject == self) {
207-
FlutterTextInputView.backingTextInputAccessibilityObject = nil;
206+
textInput.frame = CGRectMake(0.0, 0.0, 1.0, 1.0);
207+
} else if (textInput.backingTextInputAccessibilityObject == self) {
208+
textInput.backingTextInputAccessibilityObject = nil;
208209
}
209210
}
210211

0 commit comments

Comments
 (0)