4

I have a view where a UITextView always has focus. What I want to do is extend the built-in undo/redo behavior to support undo/redo for when I programmatically set the text (e.g., for when I clear it, by setting it to @"").

Since only the firstResponder gets undo/redo events, I thought I'd simply use the UITextView's undoManager property to create my invocations. e.g.,

// Before clearing the text...
[[self.myTextView.undoManager prepareWithInvocationTarget:self] undoClear:self.myTextView.text]; 
[self.myTextView.undoManager setActionName:@"Clear"];

// ...

-(void)undoClear:(NSString *)textToRestore
{
    self.myTextView.text = textToRestore;
    if ([self.myTextView.undoManager isUndoing])
    {
      // Prepare the redo.
      [[self.myTextView.undoManager prepareWithInvocationTarget:self] undoClear:@""];  
    }
}

Unfortunately, this is not working. It:

  1. Introduces an empty item into the undo stack ("Undo")
  2. The "Undo Clear" gets added after that item (if I tap "Undo", I see "Undo Clear")
  3. Undo Clear and Redo Clear work, however, then I see "Undo Clear" again and it doesn't work from there on.

Any ideas? Am I approaching this wrong?

Update: It seems like I've figured out the empty undo item issue: it happens when I set the text of the UITextView after I've called prepareWithInvocationTarget. If I call it before, it doesn't happen. Funny thing is, the empty item isn't pushed onto the undo stack if I don't call prepareWithInvocationTarget (i.e., normally, when I set the text of a UITextView).

Aral Balkan
  • 5,981
  • 3
  • 21
  • 24

3 Answers3

3

OK, figured it out:

The issue with #1 is as outlined in the update to my original post.

Issues #2 and #3 were just me using the NSUndoManager incorrectly. My final unClear method (which gets called on undos is as follows:

-(void)undoClear:(NSString *)textToRestore
{   
    NSString *textBackup = [self.myTextView.text copy];

    self.myTextView.text = textToRestore;

    if ([self.myTextView.undoManager isUndoing])
    {
        // Prepare the redo.
        [[self.myTextView.undoManager prepareWithInvocationTarget:self] undoClear:@""];     
    }
    else if ([self.myTextView.undoManager isRedoing])
    {
        [[self.myTextView.undoManager prepareWithInvocationTarget:self] undoClear:textBackup];
    }

    [textBackup release];
}

It's working as it should now.

Aral Balkan
  • 5,981
  • 3
  • 21
  • 24
  • hi, i think my problem is similar with this, can u tell me, in what method you call undoClear method? thank you – R. Dewi Jan 26 '11 at 03:48
  • @Risma: Just before you set the textView's content. – Anh Mar 16 '11 at 10:04
  • 1
    One problem I have though, when trying to implement this method, is that all prior undos get erased. So if they're typing away, then tap "clear" and want to undo twice, it'll only just get them back to what their text content was before. – philipkd Sep 07 '11 at 01:47
  • Instead of the above, which only deals with 'clearing' the field, the following will work with the field's undo/redo stack. Simply call '[self changeTextValue: @"new value"]' for all your changes: - (void) changeTextValue: (NSString*) newVal { NSString *oldNotes = [self.txtViewNotes.text copy]; [self.txtViewNotes setText: newVal]; [[self.txtViewNotes.undoManager prepareWithInvocationTarget:self] changeTextValue: oldNotes]; } – strange May 16 '14 at 00:35
1

You can change your textview to whatever text you want and preserve undo and redo manager's memory stacks. self.content is UITextView.

-(void) yourClearAllButtonIsPressed
{
    [[self.content.undoManager prepareWithInvocationTarget:self] undoClear:self.content.text];
    [self.content setText:@""];//Or something else you want to change your textview to
}

}

//just one, my method I created

-(void) undoClearBlablabla:(NSString*) text
    {
        [[self.content.undoManager prepareWithInvocationTarget:self] undoClear:self.content.text];
        [self.content setText:text];
    }
coolcool1994
  • 3,704
  • 4
  • 39
  • 43
0

I'm not sue how many Text Fields you are working with, though textBackup is not getting retained by prepareWithInvocationTarget: How is it still in the Undo Stack several clear's later? Seems like it might still be there after the first one, though not after the 2nd one or after the Autorelease pool flushes.

scooter133
  • 1,297
  • 1
  • 15
  • 29