Iphone – Undo typing in UITextView


I'm using iPhone SDK 3.0.

Say, I enabled 'shake to undo' by setting application.applicationSupportsShakeToEdit to YES in my app delegate. I created an UITextView in RootViewController and had it become first responder upon app launch.

I use - (BOOL)textView:(UITextView *)aTextView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text to make changes to the text view according to what the user types. The problem is that it messed up the default undoing action. Shaking the device no longer display 'Undo Typing'.

I tried to create an undo manager myself but have yet to success. Nothing shows up when I shake my device, even though I had the undo manager registered some actionprepareWithInvocationTarget.

Anyone has an idea?

EDIT: I have checked WriteRoom for iPhone's behavior and also TextExpander Touch SDK example code, both of which display the Undo option upon shaking until an auto-completion is made. That is, when a change is commited to the textView via setText:, I guess. So, changing the textView's content indeed has some effects on the undo mechanism.

EDIT 2: The question is that: How could I incorporate the undo feature in this case?

Best Solution

How to incorporate your own undo? Here is the simplest approach possible.

NOTE: this does not respond to shake gesture in simulator.


@interface UndoTestViewController : UIViewController <UITextViewDelegate, UIAccelerometerDelegate>{
    IBOutlet UITextView *textview;
    NSString *previousText;
    BOOL showingAlertView;
@property (nonatomic,retain) NSString *previousText;


@implementation UndoTestViewController
@synthesize previousText;

- (void)viewDidLoad {
    [super viewDidLoad];

    //disable built-in undo
    [UIApplication sharedApplication].applicationSupportsShakeToEdit = NO;

    showingAlertView = NO;

    [UIAccelerometer sharedAccelerometer].delegate = self; 
    [UIAccelerometer sharedAccelerometer].updateInterval = kUpdateInterval;

    //set initial undo text
    self.previousText = textview.text;

#pragma mark UITextViewDelegate
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {

    //save text before making change
    self.previousText = textView.text;

    //changing text in some way...
    textView.text = [NSString stringWithFormat:@"prepending text %@",textView.text];
    [textView resignFirstResponder];
    return YES;
#pragma mark -

#pragma mark UIAccelerometerDelegate
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration { 

    if( showingAlertView ) return;

    if ( acceleration.x > kAccelerationThreshold || 
        acceleration.y > kAccelerationThreshold || 
        acceleration.z > kAccelerationThreshold ) {

        showingAlertView = YES;
        NSLog(@"x: %f y:%f z: %f", acceleration.x, acceleration.y, acceleration.z);

        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:@"" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Undo Typing", nil];
        alert.delegate = self;
        [alert show];
        [alert release];
#pragma mark -

#pragma mark UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if( buttonIndex == 1 ) {
        textview.text = self.previousText;
    showingAlertView = NO;
#pragma mark -