Permalink
Browse files

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
  • Loading branch information...
shergin authored and facebook-github-bot committed Jul 18, 2017
1 parent d69e60b commit 4ff3e101ac2831a5ca64acf9aa3a868c3120e576
Showing with 28 additions and 18 deletions.
  1. +28 −2 Libraries/Text/RCTBackedTextInputDelegateAdapter.m
  2. +0 −10 Libraries/Text/RCTTextField.m
  3. +0 −6 Libraries/Text/RCTTextView.m
@@ -19,6 +19,7 @@ @interface RCTBackedTextFieldDelegateAdapter () <UITextFieldDelegate>
@implementation RCTBackedTextFieldDelegateAdapter {
__weak UITextField<RCTBackedTextInputViewProtocol> *_backedTextInput;
__unsafe_unretained UITextField<RCTBackedTextInputViewProtocol> *_unsafeBackedTextInput;
BOOL _textDidChangeIsComing;
}
- (instancetype)initWithTextField:(UITextField<RCTBackedTextInputViewProtocol> *)backedTextInput
@@ -65,12 +66,23 @@ - (BOOL)textFieldShouldEndEditing:(__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];
}
- (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
@@ -103,6 +115,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath
- (void)textFieldDidChange
{
_textDidChangeIsComing = NO;
[_backedTextInput.textInputDelegate textInputDidChange];
}
@@ -130,6 +143,7 @@ @interface RCTBackedTextViewDelegateAdapter () <UITextViewDelegate>
@implementation RCTBackedTextViewDelegateAdapter {
__weak UITextView<RCTBackedTextInputViewProtocol> *_backedTextInput;
BOOL _textDidChangeIsComing;
}
- (instancetype)initWithTextView:(UITextView<RCTBackedTextInputViewProtocol> *)backedTextInput
@@ -161,6 +175,13 @@ - (BOOL)textViewShouldEndEditing:(__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];
}
@@ -175,11 +196,16 @@ - (BOOL)textView:(__unused UITextView *)textView shouldChangeTextInRange:(NSRang
}
}
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
{
_textDidChangeIsComing = NO;
[_backedTextInput.textInputDelegate textInputDidChange];
}
@@ -28,7 +28,6 @@ @implementation RCTTextField
{
RCTUITextField *_backedTextInput;
BOOL _submitted;
NSString *_finalText;
CGSize _previousContentSize;
}
@@ -164,7 +163,6 @@ - (void)textInputDidChange
- (BOOL)textInputShouldEndEditing
{
_finalText = _backedTextInput.text;
return YES;
}
@@ -176,14 +174,6 @@ - (void)textInputDidEndEditing
key:nil
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
reactTag:self.reactTag
text:_backedTextInput.text
@@ -397,12 +397,6 @@ - (BOOL)textInputShouldEndEditing
- (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
reactTag:self.reactTag
text:_backedTextInput.text

0 comments on commit 4ff3e10

Please sign in to comment.