diff --git a/Libraries/Text/TextInput/RCTBaseTextInputView.m b/Libraries/Text/TextInput/RCTBaseTextInputView.m index d619e5b5640a14..9965f9ca2972c0 100644 --- a/Libraries/Text/TextInput/RCTBaseTextInputView.m +++ b/Libraries/Text/TextInput/RCTBaseTextInputView.m @@ -397,6 +397,14 @@ - (NSString *)textInputShouldChangeText:(NSString *)text inRange:(NSRange)range if (text.length > allowedLength) { // If we typed/pasted more than one character, limit the text inputted. if (text.length > 1) { + if (allowedLength > 0) { + //make sure unicode characters that are longer than 16 bits (such as emojis) are not cut off + NSRange cutOffCharacterRange = [text rangeOfComposedCharacterSequenceAtIndex: allowedLength - 1]; + if (cutOffCharacterRange.location + cutOffCharacterRange.length > allowedLength) { + //the character at the length limit takes more than 16bits, truncation should end at the character before + allowedLength = cutOffCharacterRange.location; + } + } // Truncate the input string so the result is exactly maxLength NSString *limitedString = [text substringToIndex:allowedLength]; NSMutableAttributedString *newAttributedText = [backedTextInputView.attributedText mutableCopy]; diff --git a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index e9e59a9235aa78..0018c787236ee1 100644 --- a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -335,6 +335,15 @@ - (NSString *)textInputShouldChangeText:(NSString *)text inRange:(NSRange)range if (props.maxLength) { NSInteger allowedLength = props.maxLength - _backedTextInputView.attributedText.string.length + range.length; + if (allowedLength > 0 && text.length > allowedLength) { + // make sure unicode characters that are longer than 16 bits (such as emojis) are not cut off + NSRange cutOffCharacterRange = [text rangeOfComposedCharacterSequenceAtIndex:allowedLength - 1]; + if (cutOffCharacterRange.location + cutOffCharacterRange.length > allowedLength) { + // the character at the length limit takes more than 16bits, truncation should end at the character before + allowedLength = cutOffCharacterRange.location; + } + } + if (allowedLength <= 0) { return nil; }