Skip to content

Commit f77aa4e

Browse files
Igor Mandriginfacebook-github-bot
Igor Mandrigin
authored andcommitted
Avoid using -[UITextView setAttributedString:] while user is typing (#19809)
Summary: iOS-specific. For languages with complex input (such as Japanese or Chinese), a user has to type multiple characters that are then merged into a single one. If `-[UITextView setAttributedString:]` is used while the user is still typing, it resets the input and characters are not being treated as typed together. This PR avoids calling this method if possible, replacing it by just copying the attributes if the string has not been changed. That preserves the state and user can continue to type Korean or Chinese characters. Fixes #19339 <!-- Required: Write your motivation here. If this PR fixes an issue, type "Fixes #issueNumber" to automatically close the issue when the PR is merged. --> <!-- Required: Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Bonus points for screenshots and videos! --> Essentially, the steps to reproduce are described in [the issue](#19339): 1. Type some Korean characters in TextInput, such as "하늘" (buttons `ㅎ`,`ㅏ`,`ㄴ`,`ㅡ`,`ㄹ`). 2. Then move the cursor to the beginning of the text, type "파란" (buttons `ㅍ`,`ㅏ`,`ㄹ`,`ㅏ`,`ㄴ`) this time. **Behaviour before this fix (broken)** Actual text: `ㅍㅏㄹㅏㄴ하늘`. Expected text: `파란하늘`. Characters aren't combined properly. ![ezgif com-resize](https://user-images.githubusercontent.com/466427/41613572-4256dda8-73f6-11e8-99a9-0ab833202b95.gif) **Behaviour after this fix (correct)** Actual text: `파란하늘`. Expected text: `파란하늘`. Characters are combined, the same behaviour is in vanilla iOS `UITextView`. ![input-with-fix](https://user-images.githubusercontent.com/466427/41613526-1aae2284-73f6-11e8-87f2-c1cef51cd83a.gif) <!-- Does this PR require a documentation change? Create a PR at https://github.com/facebook/react-native-website and add a link to it here. --> <!-- Required. Help reviewers and the release process by writing your own release notes. See below for an example. --> [IOS] [BUGFIX] [TextView] - Fix Korean/Chinese/Japanese input for multiline TextView on iOS. <!-- **INTERNAL and MINOR tagged notes will not be included in the next version's final release notes.** CATEGORY [----------] TYPE [ CLI ] [-------------] LOCATION [ DOCS ] [ BREAKING ] [-------------] [ GENERAL ] [ BUGFIX ] [ {Component} ] [ INTERNAL ] [ ENHANCEMENT ] [ {Filename} ] [ IOS ] [ FEATURE ] [ {Directory} ] |-----------| [ ANDROID ] [ MINOR ] [ {Framework} ] - | {Message} | [----------] [-------------] [-------------] |-----------| EXAMPLES: [IOS] [BREAKING] [FlatList] - Change a thing that breaks other things [ANDROID] [BUGFIX] [TextInput] - Did a thing to TextInput [CLI] [FEATURE] [local-cli/info/info.js] - CLI easier to do things with [DOCS] [BUGFIX] [GettingStarted.md] - Accidentally a thing/word [GENERAL] [ENHANCEMENT] [Yoga] - Added new yoga thing/position [INTERNAL] [FEATURE] [./scripts] - Added thing to script that nobody will see --> Pull Request resolved: #19809 Differential Revision: D13326614 Pulled By: shergin fbshipit-source-id: 6a5cab3f7290f0f623a6f4c29353a573eb321b0b
1 parent 04af674 commit f77aa4e

File tree

1 file changed

+31
-1
lines changed

1 file changed

+31
-1
lines changed

Libraries/Text/TextInput/Multiline/RCTUITextView.m

+31-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,21 @@ - (void)setTextAlignment:(NSTextAlignment)textAlignment
110110

111111
- (void)setAttributedText:(NSAttributedString *)attributedText
112112
{
113-
[super setAttributedText:attributedText];
113+
// Using `setAttributedString:` while user is typing breaks some internal mechanics
114+
// when entering complex input languages such as Chinese, Korean or Japanese.
115+
// see: https://github.com/facebook/react-native/issues/19339
116+
117+
// We try to avoid calling this method as much as we can.
118+
// If the text has changed, there is nothing we can do.
119+
if (![super.attributedText.string isEqualToString:attributedText.string]) {
120+
[super setAttributedText:attributedText];
121+
} else {
122+
// But if the text is preserved, we just copying the attributes from the source string.
123+
if (![super.attributedText isEqualToAttributedString:attributedText]) {
124+
[self copyTextAttributesFrom:attributedText];
125+
}
126+
}
127+
114128
[self textDidChange];
115129
}
116130

@@ -241,4 +255,20 @@ - (void)invalidatePlaceholderVisibility
241255
_placeholderView.hidden = !isVisible;
242256
}
243257

258+
#pragma mark - Utility Methods
259+
260+
- (void)copyTextAttributesFrom:(NSAttributedString *)sourceString
261+
{
262+
[self.textStorage beginEditing];
263+
264+
NSTextStorage *textStorage = self.textStorage;
265+
[sourceString enumerateAttributesInRange:NSMakeRange(0, sourceString.length)
266+
options:NSAttributedStringEnumerationReverse
267+
usingBlock:^(NSDictionary<NSAttributedStringKey,id> * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
268+
[textStorage setAttributes:attrs range:range];
269+
}];
270+
271+
[self.textStorage endEditing];
272+
}
273+
244274
@end

0 commit comments

Comments
 (0)