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 protected])
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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com
This email sent to [email protected]