TextInput: Hacks related to missed `textInputDidChange` were moved to adapter
Summary: iOS has tendency to skip `textInputDidChange` event (irregulary, across all dispatch ways: target-action, delegate, notification center) when text input looses focus. Usually it happens when autocorrection applies some changes automatically on loosing focus, but I think these are bunch of different cases. So, the workaround is pretty simple: if there was no `textInputDidChange` event between `shouldChangeText` and `didEndEditing`, we create it manually. Previously these workaround complicate our business logic, now they was decoupled in separate adapter. Reviewed By: mmmulani Differential Revision: D5317651 fbshipit-source-id: 138143213e8752fe9682229c51685aef614c00dd
This commit is contained in:
parent
d69e60bb7a
commit
4ff3e101ac
|
@ -19,6 +19,7 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
||||||
@implementation RCTBackedTextFieldDelegateAdapter {
|
@implementation RCTBackedTextFieldDelegateAdapter {
|
||||||
__weak UITextField<RCTBackedTextInputViewProtocol> *_backedTextInput;
|
__weak UITextField<RCTBackedTextInputViewProtocol> *_backedTextInput;
|
||||||
__unsafe_unretained UITextField<RCTBackedTextInputViewProtocol> *_unsafeBackedTextInput;
|
__unsafe_unretained UITextField<RCTBackedTextInputViewProtocol> *_unsafeBackedTextInput;
|
||||||
|
BOOL _textDidChangeIsComing;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithTextField:(UITextField<RCTBackedTextInputViewProtocol> *)backedTextInput
|
- (instancetype)initWithTextField:(UITextField<RCTBackedTextInputViewProtocol> *)backedTextInput
|
||||||
|
@ -65,12 +66,23 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
||||||
|
|
||||||
- (void)textFieldDidEndEditing:(__unused UITextField *)textField
|
- (void)textFieldDidEndEditing:(__unused UITextField *)textField
|
||||||
{
|
{
|
||||||
|
if (_textDidChangeIsComing) {
|
||||||
|
// iOS does't call `textViewDidChange:` delegate method if the change was happened because of autocorrection
|
||||||
|
// which was triggered by losing focus. So, we call it manually.
|
||||||
|
_textDidChangeIsComing = NO;
|
||||||
|
[_backedTextInput.textInputDelegate textInputDidChange];
|
||||||
|
}
|
||||||
|
|
||||||
[_backedTextInput.textInputDelegate textInputDidEndEditing];
|
[_backedTextInput.textInputDelegate textInputDidEndEditing];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)textField:(__unused UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
|
- (BOOL)textField:(__unused UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
|
||||||
{
|
{
|
||||||
return [_backedTextInput.textInputDelegate textInputShouldChangeTextInRange:range replacementText:string];
|
BOOL result = [_backedTextInput.textInputDelegate textInputShouldChangeTextInRange:range replacementText:string];
|
||||||
|
if (result) {
|
||||||
|
_textDidChangeIsComing = YES;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField
|
- (BOOL)textFieldShouldReturn:(UITextField *)textField
|
||||||
|
@ -103,6 +115,7 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
||||||
|
|
||||||
- (void)textFieldDidChange
|
- (void)textFieldDidChange
|
||||||
{
|
{
|
||||||
|
_textDidChangeIsComing = NO;
|
||||||
[_backedTextInput.textInputDelegate textInputDidChange];
|
[_backedTextInput.textInputDelegate textInputDidChange];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +143,7 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
||||||
|
|
||||||
@implementation RCTBackedTextViewDelegateAdapter {
|
@implementation RCTBackedTextViewDelegateAdapter {
|
||||||
__weak UITextView<RCTBackedTextInputViewProtocol> *_backedTextInput;
|
__weak UITextView<RCTBackedTextInputViewProtocol> *_backedTextInput;
|
||||||
|
BOOL _textDidChangeIsComing;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithTextView:(UITextView<RCTBackedTextInputViewProtocol> *)backedTextInput
|
- (instancetype)initWithTextView:(UITextView<RCTBackedTextInputViewProtocol> *)backedTextInput
|
||||||
|
@ -161,6 +175,13 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
||||||
|
|
||||||
- (void)textViewDidEndEditing:(__unused UITextView *)textView
|
- (void)textViewDidEndEditing:(__unused UITextView *)textView
|
||||||
{
|
{
|
||||||
|
if (_textDidChangeIsComing) {
|
||||||
|
// iOS does't call `textViewDidChange:` delegate method if the change was happened because of autocorrection
|
||||||
|
// which was triggered by losing focus. So, we call it manually.
|
||||||
|
_textDidChangeIsComing = NO;
|
||||||
|
[_backedTextInput.textInputDelegate textInputDidChange];
|
||||||
|
}
|
||||||
|
|
||||||
[_backedTextInput.textInputDelegate textInputDidEndEditing];
|
[_backedTextInput.textInputDelegate textInputDidEndEditing];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,11 +196,16 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [_backedTextInput.textInputDelegate textInputShouldChangeTextInRange:range replacementText:text];
|
BOOL result = [_backedTextInput.textInputDelegate textInputShouldChangeTextInRange:range replacementText:text];
|
||||||
|
if (result) {
|
||||||
|
_textDidChangeIsComing = YES;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)textViewDidChange:(__unused UITextView *)textView
|
- (void)textViewDidChange:(__unused UITextView *)textView
|
||||||
{
|
{
|
||||||
|
_textDidChangeIsComing = NO;
|
||||||
[_backedTextInput.textInputDelegate textInputDidChange];
|
[_backedTextInput.textInputDelegate textInputDidChange];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
{
|
{
|
||||||
RCTUITextField *_backedTextInput;
|
RCTUITextField *_backedTextInput;
|
||||||
BOOL _submitted;
|
BOOL _submitted;
|
||||||
NSString *_finalText;
|
|
||||||
CGSize _previousContentSize;
|
CGSize _previousContentSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +163,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||||
|
|
||||||
- (BOOL)textInputShouldEndEditing
|
- (BOOL)textInputShouldEndEditing
|
||||||
{
|
{
|
||||||
_finalText = _backedTextInput.text;
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,14 +174,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||||
key:nil
|
key:nil
|
||||||
eventCount:_nativeEventCount];
|
eventCount:_nativeEventCount];
|
||||||
|
|
||||||
if (![_finalText isEqualToString:_backedTextInput.text]) {
|
|
||||||
_finalText = nil;
|
|
||||||
// iOS does't send event `UIControlEventEditingChanged` if the change was happened because of autocorrection
|
|
||||||
// which was triggered by loosing focus. We assume that if `text` was changed in the middle of loosing focus process,
|
|
||||||
// we did not receive that event. So, we call `textFieldDidChange` manually.
|
|
||||||
[self textInputDidChange];
|
|
||||||
}
|
|
||||||
|
|
||||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd
|
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd
|
||||||
reactTag:self.reactTag
|
reactTag:self.reactTag
|
||||||
text:_backedTextInput.text
|
text:_backedTextInput.text
|
||||||
|
|
|
@ -397,12 +397,6 @@ static BOOL findMismatch(NSString *first, NSString *second, NSRange *firstRange,
|
||||||
|
|
||||||
- (void)textInputDidEndEditing
|
- (void)textInputDidEndEditing
|
||||||
{
|
{
|
||||||
if (_nativeUpdatesInFlight) {
|
|
||||||
// iOS does't call `textViewDidChange:` delegate method if the change was happened because of autocorrection
|
|
||||||
// which was triggered by loosing focus. So, we call it manually.
|
|
||||||
[self textInputDidChange];
|
|
||||||
}
|
|
||||||
|
|
||||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd
|
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd
|
||||||
reactTag:self.reactTag
|
reactTag:self.reactTag
|
||||||
text:_backedTextInput.text
|
text:_backedTextInput.text
|
||||||
|
|
Loading…
Reference in New Issue