AVS
AVS

Reputation: 544

Selected Text Replacement Bug UITextView Mac Catalyst

Starting with the macOS version 14.x.x and TextKit1, selecting multiple lines of text triggers a text replacement bug: some of the text on one of the selected lines inadvertently replaces a portion of the selected text.

For example, the bug is exhibited when selecting the following lines:

Carnaroli, Maratelli, or Vialone Nano are best
Vialone Nano cooks quickly – watch it! It also absorbs condiments nicely.
Avoid Baldo, Originario, Ribe and Roma

To trigger the bug, select the three line paragraph using either the cursor or shift with arrow keys. Notice that a portion of the selected text was replaced. Command-Z to undo will allow you to repeat the undesired behavior.

In this case, "e Nano cooks quickly - " is replaced by "Baldo, Originario, Ribe."

This does not occur with all strings or selected strings, but in cases where it does occur, it is perfectly reproducible. It does not occur on iOS. Pasteboard contents are irrelevant. After triggering the bug repeatedly, at some point it stops occurring.

Why does this bug occur? How can it be fixed?

Screen grabs showcasing the behavior

Before selection: before selection

After selection: after selection

Sample code to recreate the issue

@interface TestNoteViewController ()

@end


@implementation TestNoteViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self createTextView];
}

- (void)createTextView {
    
    NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:self.note.text
                                                                     attributes:nil];
    
    NSTextStorage *textStorage = [NSTextStorage new];
    [textStorage appendAttributedString:attrString];
    
    CGRect newTextViewRect = self.view.bounds;
    
    // Create the layout manager
    NSLayoutManager *layoutManager = [NSLayoutManager new];
    [textStorage addLayoutManager:layoutManager];
    
    // Create a text container
    NSTextContainer *container = [[NSTextContainer alloc] initWithSize:CGSizeMake(newTextViewRect.size.width, CGFLOAT_MAX)];
    [layoutManager addTextContainer:container];
    
    
    // Create and place a text view
    UITextView *textView = [[UITextView alloc] initWithFrame:newTextViewRect
                                               textContainer:container];
    [self.view addSubview:textView];
    textView.translatesAutoresizingMaskIntoConstraints = NO;
    UILayoutGuide *safeArea = textView.superview.safeAreaLayoutGuide;
    [textView.leadingAnchor constraintEqualToAnchor:safeArea.leadingAnchor].active = YES;
    [textView.trailingAnchor constraintEqualToAnchor:safeArea.trailingAnchor].active = YES;
    [textView.topAnchor constraintEqualToAnchor:safeArea.topAnchor].active = YES;
    [textView.bottomAnchor constraintEqualToAnchor:textView.superview.bottomAnchor].active = YES;
}

@end

Sample code to recreate the issue WITHOUT the text container and layout manager, which also exhibits the bug

@interface TestNoteViewController ()

@end


@implementation TestNoteViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self createTextView];
}

- (void)createTextView {
    
    UITextView *textView = [UITextView new];
    textView.text = self.note.text;
    [self.view addSubview:textView];
    textView.translatesAutoresizingMaskIntoConstraints = NO;
    UILayoutGuide *safeArea = textView.superview.safeAreaLayoutGuide;
    [textView.leadingAnchor constraintEqualToAnchor:safeArea.leadingAnchor].active = YES;
    [textView.trailingAnchor constraintEqualToAnchor:safeArea.trailingAnchor].active = YES;
    [textView.topAnchor constraintEqualToAnchor:safeArea.topAnchor].active = YES;
    [textView.bottomAnchor constraintEqualToAnchor:textView.superview.bottomAnchor].active = YES;
}

@end

Upvotes: 0

Views: 74

Answers (0)

Related Questions