Search code examples
objective-cnstextfieldnsundomanager

Undo manager with multiple fields


Hello I have an application, it's very basic but has some fields for an address, so city, zip code, state etc...

I want it so the user can undo the content across multiple fields, currently the default undo only works on the field you are using. I am using NSTextFields for my fields.

I assume I need to use UndoManager but I am not sure how to implement it on my fields, my fields have ID's. Is there any documentation on how to do this, none of the fields have any functions - I just grab the data from all of the fields on one of my button events.

I don't know swift so please responsd in Obj-c so I can undertstand what needs to be done.


Solution

  • Here's a small example to get you started.

    1. Start with the Xcode macOS App template.
    2. Add the code below to AppDelegate.m.
    3. Add some controls (NSTextField or another subclass of NSControl) to the window.
    4. Connect the action of the controls to controlAction:.
    @interface AppDelegate ()
    
    @property (strong) IBOutlet NSWindow *window;
    
    // values before editing, the key is the identifier of the control
    @property (strong) NSMutableDictionary *data;
    
    @end
    
    
    @implementation AppDelegate
    
    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
        self.data = [[NSMutableDictionary alloc] init];
    }
    
    - (void)undoControl:(NSControl *)control oldValue:(id)oldValue newValue:(id)newValue {
        // undo
        NSString *controlID = control.identifier;
        [control setObjectValue:oldValue];
        self.data[controlID] = oldValue;
        if (control.acceptsFirstResponder)
            [self.window makeFirstResponder: control];
        // register redo
        NSLog(@"register redo %@ old:'%@' new:'%@'", controlID, oldValue, newValue);
        [[self.window.undoManager prepareWithInvocationTarget:self] undoControl:control
            oldValue:newValue newValue:oldValue];
    }
    
    - (IBAction)controlAction:(NSControl *)control {
        NSString *controlID = control.identifier;
        id oldValue = self.data[controlID];
        id newValue = control.objectValue;
        if (oldValue != newValue && ![oldValue isEqual:newValue]) {
            // register undo
            NSLog(@"register undo %@ old:'%@' new:'%@'", controlID, oldValue, newValue);
            [[self.window.undoManager prepareWithInvocationTarget:self] undoControl:control
                oldValue:oldValue newValue:newValue];
            self.data[controlID] = newValue;
        }
    }
    
    @end