From 05ebf771752c83528d4757dfc1656213159730b0 Mon Sep 17 00:00:00 2001 From: Igor Mandrigin Date: Mon, 4 Feb 2019 15:00:58 -0800 Subject: [PATCH] Apply the fix for CJK languages on single-line text fields. (#22546) Summary: Follow-up to https://github.com/facebook/react-native/pull/19809 This fix generalizes the `setAttributedString:` fix to single-line text fields. Fixes #19339 _Pull requests that expand test coverage are more likely to get reviewed. Add a test case whenever possible!_ Pull Request resolved: https://github.com/facebook/react-native/pull/22546 Differential Revision: D13948951 Pulled By: shergin fbshipit-source-id: 67992c02b32f33f6d61fac4554e4f46b973262c1 --- .../TextInput/Singleline/RCTUITextField.m | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/Libraries/Text/TextInput/Singleline/RCTUITextField.m b/Libraries/Text/TextInput/Singleline/RCTUITextField.m index aac8ec51ac4fa6..411e2872b667a5 100644 --- a/Libraries/Text/TextInput/Singleline/RCTUITextField.m +++ b/Libraries/Text/TextInput/Singleline/RCTUITextField.m @@ -14,6 +14,7 @@ @implementation RCTUITextField { RCTBackedTextFieldDelegateAdapter *_textInputDelegateAdapter; + NSMutableAttributedString *_attributesHolder; } - (instancetype)initWithFrame:(CGRect)frame @@ -25,6 +26,7 @@ - (instancetype)initWithFrame:(CGRect)frame object:self]; _textInputDelegateAdapter = [[RCTBackedTextFieldDelegateAdapter alloc] initWithTextField:self]; + _attributesHolder = [[NSMutableAttributedString alloc] init]; } return self; @@ -107,6 +109,49 @@ - (CGRect)caretRectForPosition:(UITextPosition *)position return [super caretRectForPosition:position]; } +#pragma mark - Fix for CJK Languages + +/* + * The workaround to fix inputting complex locales (like CJK languages). + * When we use `setAttrbutedText:` while user is inputting text in a complex + * locale (like Chinese, Japanese or Korean), some internal state breaks and + * input stops working. + * + * To workaround that, we don't skip underlying attributedString in the text + * field if only attributes were changed. We keep track of these attributes in + * a local variable. + * + * There are two methods that are altered by this workaround: + * + * (1) `-setAttributedText:` + * Applies the attributed string change to a local variable `_attributesHolder` instead of calling `-[super setAttributedText:]`. + * If new attributed text differs from the existing one only in attributes, + * skips `-[super setAttributedText:`] completely. + * + * (2) `-attributedText` + * Return `_attributesHolder` context. + * Updates `_atributesHolder` before returning if the underlying `super.attributedText.string` was changed. + * + */ +- (void)setAttributedText:(NSAttributedString *)attributedText +{ + BOOL textWasChanged = ![_attributesHolder.string isEqualToString:attributedText.string]; + [_attributesHolder setAttributedString:attributedText]; + + if (textWasChanged) { + [super setAttributedText:attributedText]; + } +} + +- (NSAttributedString *)attributedText +{ + if (![super.attributedText.string isEqualToString:_attributesHolder.string]) { + [_attributesHolder setAttributedString:super.attributedText]; + } + + return _attributesHolder; +} + #pragma mark - Positioning Overrides - (CGRect)textRectForBounds:(CGRect)bounds