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:
- Introduces an empty item into the undo stack ("Undo")
- The "Undo Clear" gets added after that item (if I tap "Undo", I see "Undo Clear")
- 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).
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.
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];
}
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.