diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index 4434e72321de3b1b98a283f8085bfaf19a8b89a7..854a3ff7f4c1eb789c765c1e627765b1e4d1aba4 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -1234,6 +1234,30 @@ static FlutterAutofillType autofillTypeOf(NSDictionary* configuration) { @end +@interface FlutterTextInputPlugin () +- (void)enableActiveViewAccessibility; +@end + +@interface FlutterTimerProxy : NSObject +@property(nonatomic, assign) FlutterTextInputPlugin* target; +@end + +@implementation FlutterTimerProxy + ++ (instancetype)proxyWithTarget:(FlutterTextInputPlugin*)target { + FlutterTimerProxy* proxy = [[self new] autorelease]; + if (proxy) { + proxy.target = target; + } + return proxy; +} + +- (void)enableActiveViewAccessibility { + [self.target enableActiveViewAccessibility]; +} + +@end + @interface FlutterTextInputPlugin () @property(nonatomic, strong) FlutterTextInputView* reusableInputView; @@ -1266,6 +1290,7 @@ static FlutterAutofillType autofillTypeOf(NSDictionary* configuration) { - (void)dealloc { [self hideTextInput]; + _activeView.textInputDelegate = nil; [_reusableInputView release]; [_activeView release]; [_inputHider release]; @@ -1344,15 +1369,15 @@ static FlutterAutofillType autofillTypeOf(NSDictionary* configuration) { if (!_enableFlutterTextInputViewAccessibilityTimer) { _enableFlutterTextInputViewAccessibilityTimer = [[NSTimer scheduledTimerWithTimeInterval:kUITextInputAccessibilityEnablingDelaySeconds - target:self - selector:@selector(enableActiveViewAccessibility:) + target:[FlutterTimerProxy proxyWithTarget:self] + selector:@selector(enableActiveViewAccessibility) userInfo:nil repeats:NO] retain]; } [_activeView becomeFirstResponder]; } -- (void)enableActiveViewAccessibility:(NSTimer*)time { +- (void)enableActiveViewAccessibility { if (_activeView.isFirstResponder) { _activeView.accessibilityEnabled = YES; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m index 06f53c39ff7cbe8055f4a0e99f969233925152ca..997daa2a0d48ab801c69a288d920a8c827f5b298 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m @@ -240,6 +240,18 @@ FLUTTER_ASSERT_ARC XCTAssert([[passwordView.textField description] containsString:@"TextField"]); } +- (void)testInputViewCrash { + FlutterTextInputView* activeView = nil; + @autoreleasepool { + FlutterTextInputPlugin* inputPlugin = [FlutterTextInputPlugin new]; + activeView = inputPlugin.activeView; + FlutterEngine* flutterEngine = [[FlutterEngine alloc] init]; + activeView.textInputDelegate = (id)flutterEngine; + } + XCTAssert(!activeView.textInputDelegate); + [activeView updateEditingState]; +} + - (void)ensureOnlyActiveViewCanBecomeFirstResponder { for (FlutterTextInputView* inputView in self.installedInputViews) { XCTAssertEqual(inputView.canBecomeFirstResponder, inputView == textInputPlugin.activeView);