diff --git a/ios/EnrichedTextInputView.mm b/ios/EnrichedTextInputView.mm index 82c7c7136..f4926ae85 100644 --- a/ios/EnrichedTextInputView.mm +++ b/ios/EnrichedTextInputView.mm @@ -14,6 +14,7 @@ #import "WordsUtils.h" #import "LayoutManagerExtension.h" #import "ZeroWidthSpaceUtils.h" +#import "ParagraphAttributesUtils.h" using namespace facebook::react; @@ -977,6 +978,22 @@ - (void)manageSelectionBasedChanges { [mentionStyleClass manageMentionTypingAttributes]; [mentionStyleClass manageMentionEditing]; } + + // typing attributes for empty lines selection reset + NSString *currentString = [[textView.textStorage.string copy] stringByReplacingOccurrencesOfString:@"\u200B" withString:@""]; + if(textView.selectedRange.length == 0 && [_recentlyEmittedString isEqualToString: currentString] ) { + // no string change means only a selection changed with no character changes + NSRange paragraphRange = [textView.textStorage.string paragraphRangeForRange:textView.selectedRange]; + if( + paragraphRange.length == 0 || + (paragraphRange.length == 1 && + [[NSCharacterSet newlineCharacterSet] characterIsMember:[textView.textStorage.string characterAtIndex:paragraphRange.location]]) + ) { + // user changed selection to an empty line (or empty line with a newline) + // typing attributes need to be reset + textView.typingAttributes = defaultTypingAttributes; + } + } } - (void)handleWordModificationBasedChanges:(NSString*)word inRange:(NSRange)range { @@ -1135,7 +1152,8 @@ - (bool)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range r [h1Style handleNewlinesInRange:range replacementText:text] || [h2Style handleNewlinesInRange:range replacementText:text] || [h3Style handleNewlinesInRange:range replacementText:text] || - [ZeroWidthSpaceUtils handleBackspaceInRange:range replacementText:text input:self] + [ZeroWidthSpaceUtils handleBackspaceInRange:range replacementText:text input:self] || + [ParagraphAttributesUtils handleBackspaceInRange:range replacementText:text input:self] ) { [self anyTextMayHaveBeenModified]; return NO; diff --git a/ios/utils/ParagraphAttributesUtils.h b/ios/utils/ParagraphAttributesUtils.h new file mode 100644 index 000000000..0a1fd3a3d --- /dev/null +++ b/ios/utils/ParagraphAttributesUtils.h @@ -0,0 +1,6 @@ +#import +#pragma once + +@interface ParagraphAttributesUtils : NSObject ++ (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text input:(id)input; +@end diff --git a/ios/utils/ParagraphAttributesUtils.mm b/ios/utils/ParagraphAttributesUtils.mm new file mode 100644 index 000000000..38b69c825 --- /dev/null +++ b/ios/utils/ParagraphAttributesUtils.mm @@ -0,0 +1,43 @@ +#import "ParagraphAttributesUtils.h" +#import "EnrichedTextInputView.h" +#import "ParagraphsUtils.h" +#import "TextInsertionUtils.h" + +@implementation ParagraphAttributesUtils + +// if the user backspaces the last character in a line, the iOS applies typing attributes from the previous line +// in the case of some paragraph styles it works especially bad when a list point just appears +// hence the solution - reset typing attributes ++ (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text input:(id)input { + EnrichedTextInputView *typedInput = (EnrichedTextInputView *)input; + if(typedInput == nullptr) { + return NO; + } + + // we make sure it was a backspace (text with 0 length) and it deleted something (range longer than 0) + if(text.length > 0 || range.length == 0) { + return NO; + } + + // find a non-newline range of the paragraph + NSRange paragraphRange = [typedInput->textView.textStorage.string paragraphRangeForRange:range]; + NSArray *paragraphs = [ParagraphsUtils getNonNewlineRangesIn:typedInput->textView range:paragraphRange]; + if(paragraphs.count == 0) { + return NO; + } + + NSRange nonNewlineRange = [(NSValue *)paragraphs.firstObject rangeValue]; + + // if the backspace removes the whole content of a paragraph - do the thing + if(NSEqualRanges(nonNewlineRange, range)) { + // do the replacement manually + [TextInsertionUtils replaceText:text at:range additionalAttributes:nullptr input:typedInput withSelection:YES]; + // reset typing attribtues + typedInput->textView.typingAttributes = typedInput->defaultTypingAttributes; + return YES; + } + + return NO; +} + +@end