Search code examples
objective-cmacoscocoansundomanager

NSUndoManager and replacing/skipping undo steps


Is there any way to tell undoManager that in some case the undo step has already been committed before the edit has happened?

I have an application which automatically replaces certain strings with uppercase counterparts. Trouble is, this has to be done automatically into textStorage, so when undoing the edits, text remains uppercase.

I've already written custom undo states for the operations in question, which are created in NSTextView shouldChangeTextInRange:

For example:

[[self.undoManager prepareWithInvocationTarget:self] 
       replaceCharactersInRange:
             NSMakeRange(affectedCharRange.location + affectedCharRange.length, string.length + 1) withString:string
];

I'd like the undoManager to ignore the undo step it's going to receive after the edit.


Solution

  • One approach is disabling NSUndoManager from logging undo steps by using [self.undoManager disableUndoRegistration];. Then, after the operation is done, you can enable it again with [self.undoManager enableUndoRegistration].

    This seems to be a bit dangerous approach because it can really mess up you undo stack.

    I fixed this issue by making an extra undo step before the NSTextView undo gets registered:

    [self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) {
        [self replaceCharactersInRange:undoStringRange withString:undoString];
    }];
    

    Redoing can cause harm here, though, because the whole affected string might not get registered with text view's internal methods.