How to implement undo with NSTextview
Asked Answered
M

2

9

I want to implement undo action after replacing portion of text in an NSTextView. I am replacing portion of text with following code

- (void)omeAction
{
    NSString *fullString = [self.textView string];
    NSRange selectedRange = [self.textView selectedRange];
    NSString *selectedString = [fullString substringWithRange:selectedRange];

    NSString *stringToReplace = ...;
    [[self.textView textStorage] beginEditing];
    [[self.textView  textStorage] replaceCharactersInRange:selectedRange withString:stringToReplace];
    [[self.textView textStorage] endEditing];
}

While performing undo I couldn't really undo the text replacement

Mccombs answered 31/8, 2014 at 10:5 Comment(0)
P
11

From Cocoa Text Architecture Guide: Text Editing – Text Change Notifications and Delegate Messages:

In actually making changes to the text, you must ensure that the changes are properly performed and recorded by different parts of the text system. You do this by bracketing each batch of potential changes with shouldChangeTextInRange:replacementString: and didChangeText messages. These methods ensure that the appropriate delegate messages are sent and notifications posted. …

In my experience, that includes generating the relevant undo operation.

So, you would do:

if ([self.textView shouldChangeTextInRange:selectedRange replacementString:stringToReplace])
{
    [[self.textView textStorage] beginEditing];
    [[self.textView textStorage] replaceCharactersInRange:selectedRange withString:stringToReplace];
    [[self.textView textStorage] endEditing];
    [self.textView didChangeText];
}
Proconsul answered 31/8, 2014 at 10:32 Comment(2)
apologies, didn't read the full question. I thought was this for undo in general (vague question title). Undo wasn't working at all in my case, which ended being that nstextview simply wasn't registering certain key events. DisregardAusterity
Thanks, shouldChangeTextInRange helped to kick off undo working correctlyTallinn
T
2

First I tried to solve undo and shouldChangeTextInRange:replacementString: did the trick. However I found that insertText:replacementRange: had the same effect.

[self insertText:attributedString replacementRange:range];

Or:

if ([textView shouldChangeTextInRange:range replacementString:string]) { //string
  [textStorage beginEditing];
  [textStorage replaceCharactersInRange:range withAttributedString:attributedString];
  [textStorage endEditing];
  [textView didChangeText];
}
Tallinn answered 27/10, 2017 at 9:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.