8

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

Johnykutty
  • 12,091
  • 13
  • 59
  • 100

2 Answers2

10

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];
}
Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • 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. Disregard – Charlton Provatas Mar 08 '17 at 14:56
  • Thanks, shouldChangeTextInRange helped to kick off undo working correctly – Marek H Oct 18 '17 at 18:13
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];
}
Marek H
  • 5,173
  • 3
  • 31
  • 42