Skip to content

Commit f3b8d49

Browse files
Shuhao Zhangfacebook-github-bot
Shuhao Zhang
authored andcommitted
Bug fix: <TextInput> content is reset when emoji is entered at the max length
Summary: When maxLength is defined in <TextInput>, if the last character at max length is an emoji, the content of the input is cleared: {F620865178} {F620865237} Related Github issues: #10929 #10964 ## Root cause: When NSString was created, unicode characters were 16-bit long, so Objective-C considers every unicode character as 16-bit. However, unicode was later extended to more than 16bit, for example, emojis, which causes NSString substring method cuts off at the wrong position. Example: ``` NSString *s = @"abc{emoji:1f601}"; NSInteger len = s.length; //length is 5 (as {emoji:1f601} occupies two 16-bit characters) NSString *s3 = [s substringToIndex: 3]; //s3 is "abc" NSString *s4 = [s substringToIndex: 4]; //s4 is null! ``` If string s, "abc{emoji:1f601}", is entered in <TextInput>, which has max length 4, it will truncate the string to the first 4 characters, "cutting" the emoji in half which causes encoding error and returns null. The text input is cleared. ## Solution: If the character at max length is longer than 16-bit, truncate the character BEFORE it instead. In the previous example, truncate till index 3 instead of 4. The end result will be "abc" and the emoji is dropped. ## Changelog: [iOS] [Fixed] - <TextInput> content is reset when emoji is entered at the max length Reviewed By: p-sun Differential Revision: D28821909 fbshipit-source-id: 4720d864970b554160ed5388f65b352ce95a6199
1 parent 41f145f commit f3b8d49

File tree

2 files changed

+17
-0
lines changed

2 files changed

+17
-0
lines changed

Libraries/Text/TextInput/RCTBaseTextInputView.m

+8
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,14 @@ - (NSString *)textInputShouldChangeText:(NSString *)text inRange:(NSRange)range
397397
if (text.length > allowedLength) {
398398
// If we typed/pasted more than one character, limit the text inputted.
399399
if (text.length > 1) {
400+
if (allowedLength > 0) {
401+
//make sure unicode characters that are longer than 16 bits (such as emojis) are not cut off
402+
NSRange cutOffCharacterRange = [text rangeOfComposedCharacterSequenceAtIndex: allowedLength - 1];
403+
if (cutOffCharacterRange.location + cutOffCharacterRange.length > allowedLength) {
404+
//the character at the length limit takes more than 16bits, truncation should end at the character before
405+
allowedLength = cutOffCharacterRange.location;
406+
}
407+
}
400408
// Truncate the input string so the result is exactly maxLength
401409
NSString *limitedString = [text substringToIndex:allowedLength];
402410
NSMutableAttributedString *newAttributedText = [backedTextInputView.attributedText mutableCopy];

React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm

+9
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,15 @@ - (NSString *)textInputShouldChangeText:(NSString *)text inRange:(NSRange)range
335335
if (props.maxLength) {
336336
NSInteger allowedLength = props.maxLength - _backedTextInputView.attributedText.string.length + range.length;
337337

338+
if (allowedLength > 0 && text.length > allowedLength) {
339+
// make sure unicode characters that are longer than 16 bits (such as emojis) are not cut off
340+
NSRange cutOffCharacterRange = [text rangeOfComposedCharacterSequenceAtIndex:allowedLength - 1];
341+
if (cutOffCharacterRange.location + cutOffCharacterRange.length > allowedLength) {
342+
// the character at the length limit takes more than 16bits, truncation should end at the character before
343+
allowedLength = cutOffCharacterRange.location;
344+
}
345+
}
346+
338347
if (allowedLength <= 0) {
339348
return nil;
340349
}

0 commit comments

Comments
 (0)