Re: Implementing undo in custom model object for lots of properties
Re: Implementing undo in custom model object for lots of properties
- Subject: Re: Implementing undo in custom model object for lots of properties
- From: Graham Cox <email@hidden>
- Date: Tue, 08 May 2012 09:45:14 +1000
Hi Ben,
I think this undo approach should work fine.
Another way to 'centralise' undo is to have an object listen for the KVO notifications of changes for properties it's interested in, and use the observation method to record to the undo manager. You'd still use setValue:forKey: at undo time, so it amounts to a very similar idea. I've done it that way and it works.
So your problem must be something else. Are you sure undoManager returns something?
Also, more obvious now I come to think about it, is that setValue:forKey: is not called for every property when it is set! That method can be used to call down to the setter for a named property, but if the setter is invoked directly (the more usual case), then setValue:forKey: isn't invoked. You might want to check whether that is what's happening. In which case making your undo handler a KVO observer will get around that problem, or you could override -willChangeValueForKey: instead, which is the method that causes some of the KVO "magic" to happen.
--Graham
On 08/05/2012, at 9:11 AM, Ben Kennedy wrote:
> I have a custom data model object with a number of properties of various basic types. An array of these is managed by an NSArrayController and bound to an NSTableView (as the reader might recall from my earlier thread). I am not using Core Data.
>
> I am now wiring in undo support. Were the data backed by an NSManagedObjectContext, I would get undo behaviour for free, but since there isn't, I need to write my own setters to handle it.
>
> It seems onerous, verbose and error-prone to have to implement a custom stub setter method for every property like so:
>
> - (void)setBlah:(Blah *)newBlah
> {
> if (newBlah != blah)
> {
> [undoManager registerUndoWithTarget:self selector:@selector(setBlah:) object:blah];
> [blah release];
> blah = [newBlah copy];
> }
> }
>
> Thus, I have tried to be clever by doing the following override:
>
> - (void)setValue:(id)value forKey:(NSString *)key
> {
> NSArray *undoableKeys = [NSArray arrayWithObjects: @"blah", @"foo", @"anotherProperty", nil];
>
> if ([undoableKeys containsObject:key])
> {
> [[undoManager prepareWithInvocationTarget:self] setValue:[self valueForKey:key] forKey:key];
> }
>
> [super setValue:value forKey:key];
>
> return;
> }
>
> However, the undo event does not seem to be recorded -- the Undo menu command remains ghosted. It is not clear to me why.
>
> It wouldn't surprise me to be admonished for such an abuse of setValue:forKey:, but is there a better way? I imagine this idiom must be extremely commonplace, but I could not find a clear directive.
>
> I looked to Apple's "DemoMonkey" sample for its undo practices, and it just implements the verbose boilerplate for each property. Is that really the recommended solution?
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden