diff --git a/Libraries/Text/RCTTextField.m b/Libraries/Text/RCTTextField.m index 0ea13179b..a82f55bba 100644 --- a/Libraries/Text/RCTTextField.m +++ b/Libraries/Text/RCTTextField.m @@ -48,7 +48,6 @@ @implementation RCTTextField { RCTEventDispatcher *_eventDispatcher; - BOOL _jsRequestingFirstResponder; NSInteger _nativeEventCount; BOOL _submitted; UITextRange *_previousSelectionRange; @@ -94,16 +93,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) eventCount:_nativeEventCount]; } -- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator -{ - [super didUpdateFocusInContext:context withAnimationCoordinator:coordinator]; - if(context.nextFocusedView == self) { - _jsRequestingFirstResponder = YES; - } else { - _jsRequestingFirstResponder = NO; - } -} - // This method is overridden for `onKeyPress`. The manager // will not send a keyPress for text that was pasted. - (void)paste:(id)sender @@ -285,21 +274,6 @@ static void RCTUpdatePlaceholder(RCTTextField *self) } } -- (BOOL)canBecomeFirstResponder -{ - return _jsRequestingFirstResponder; -} - -- (void)reactWillMakeFirstResponder -{ - _jsRequestingFirstResponder = YES; -} - -- (void)reactDidMakeFirstResponder -{ - _jsRequestingFirstResponder = NO; -} - - (BOOL)resignFirstResponder { BOOL result = [super resignFirstResponder]; @@ -314,6 +288,11 @@ static void RCTUpdatePlaceholder(RCTTextField *self) return result; } +- (void)didMoveToWindow +{ + [self reactFocusIfNeeded]; +} + #pragma mark - UITextFieldDelegate (Proxied) - (BOOL)shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index dc0d289fc..f4329d0ef 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -332,7 +332,7 @@ static NSAttributedString *removeReactTagFromString(NSAttributedString *string) text:self.text key:nil eventCount:_nativeEventCount]; - [self resignFirstResponder]; + [_textView resignFirstResponder]; return NO; } } @@ -541,40 +541,24 @@ static BOOL findMismatch(NSString *first, NSString *second, NSRange *firstRange, eventCount:_nativeEventCount]; } -#pragma mark - UIResponder +#pragma mark - Focus control deledation -- (BOOL)isFirstResponder +- (void)reactFocus { - return [_textView isFirstResponder]; + [_textView reactFocus]; } -- (BOOL)canBecomeFirstResponder +- (void)reactBlur { - return [_textView canBecomeFirstResponder]; + [_textView reactBlur]; } -- (void)reactWillMakeFirstResponder +- (void)didMoveToWindow { - [_textView reactWillMakeFirstResponder]; + [_textView reactFocusIfNeeded]; } -- (BOOL)becomeFirstResponder -{ - return [_textView becomeFirstResponder]; -} - -- (void)reactDidMakeFirstResponder -{ - [_textView reactDidMakeFirstResponder]; -} - -- (BOOL)resignFirstResponder -{ - [super resignFirstResponder]; - return [_textView resignFirstResponder]; -} - -#pragma mark - Content Size +#pragma mark - Content size - (CGSize)contentSize { diff --git a/Libraries/Text/RCTUITextView.m b/Libraries/Text/RCTUITextView.m index 67ae7e579..8288ea71f 100644 --- a/Libraries/Text/RCTUITextView.m +++ b/Libraries/Text/RCTUITextView.m @@ -9,9 +9,10 @@ #import "RCTUITextView.h" +#import + @implementation RCTUITextView { - BOOL _jsRequestingFirstResponder; UILabel *_placeholderView; UITextView *_detachedTextView; } @@ -69,31 +70,6 @@ static UIColor *defaultPlaceholderTextColor() [self invalidatePlaceholderVisibility]; } -#pragma mark - UIResponder - -- (void)reactWillMakeFirstResponder -{ - _jsRequestingFirstResponder = YES; -} - -- (BOOL)canBecomeFirstResponder -{ - return _jsRequestingFirstResponder; -} - -- (void)reactDidMakeFirstResponder -{ - _jsRequestingFirstResponder = NO; -} - -- (void)didMoveToWindow -{ - if (_jsRequestingFirstResponder) { - [self becomeFirstResponder]; - [self reactDidMakeFirstResponder]; - } -} - #pragma mark - Overrides - (void)setFont:(UIFont *)font diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index bd1e72bb6..ea7e59803 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -1077,10 +1077,7 @@ RCT_EXPORT_METHOD(focus:(nonnull NSNumber *)reactTag) { [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { UIView *newResponder = viewRegistry[reactTag]; - [newResponder reactWillMakeFirstResponder]; - if ([newResponder becomeFirstResponder]) { - [newResponder reactDidMakeFirstResponder]; - } + [newResponder reactFocus]; }]; } @@ -1088,7 +1085,7 @@ RCT_EXPORT_METHOD(blur:(nonnull NSNumber *)reactTag) { [self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry){ UIView *currentResponder = viewRegistry[reactTag]; - [currentResponder resignFirstResponder]; + [currentResponder reactBlur]; }]; } diff --git a/React/Views/UIView+React.h b/React/Views/UIView+React.h index a5950af7b..899110438 100644 --- a/React/Views/UIView+React.h +++ b/React/Views/UIView+React.h @@ -71,11 +71,16 @@ */ - (void)reactAddControllerToClosestParent:(UIViewController *)controller; +/** + * Focus manipulation. + */ +- (void)reactFocus; +- (void)reactFocusIfNeeded; +- (void)reactBlur; + /** * Responder overrides - to be deprecated. */ -- (void)reactWillMakeFirstResponder; -- (void)reactDidMakeFirstResponder; - (BOOL)reactRespondsToTouch:(UITouch *)touch; #if RCT_DEV diff --git a/React/Views/UIView+React.m b/React/Views/UIView+React.m index 43502719f..4489fde30 100644 --- a/React/Views/UIView+React.m +++ b/React/Views/UIView+React.m @@ -211,11 +211,40 @@ } } +/** + * Focus manipulation. + */ +- (BOOL)reactIsFocusNeeded +{ + return [(NSNumber *)objc_getAssociatedObject(self, @selector(reactIsFocusNeeded)) boolValue]; +} + +- (void)setReactIsFocusNeeded:(BOOL)isFocusNeeded +{ + objc_setAssociatedObject(self, @selector(reactIsFocusNeeded), @(isFocusNeeded), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void)reactFocus { + if (![self becomeFirstResponder]) { + self.reactIsFocusNeeded = YES; + } +} + +- (void)reactFocusIfNeeded { + if (self.reactIsFocusNeeded) { + if ([self becomeFirstResponder]) { + self.reactIsFocusNeeded = NO; + } + } +} + +- (void)reactBlur { + [self resignFirstResponder]; +} + /** * Responder overrides - to be deprecated. */ -- (void)reactWillMakeFirstResponder {}; -- (void)reactDidMakeFirstResponder {}; - (BOOL)reactRespondsToTouch:(__unused UITouch *)touch { return YES;