Re: NSUndoManager setActionName: oddity
Re: NSUndoManager setActionName: oddity
- Subject: Re: NSUndoManager setActionName: oddity
- From: Conrad Shultz <email@hidden>
- Date: Mon, 02 Jul 2012 10:10:50 -0700
Thanks Michael.
I was aware of the run loop considerations for NSUndoManager but hadn't seen that note on the NSArrayController method. Thanks for pointing that out.
What confuses me, though, is that, contrary to the identical note for -remove:, my approach DOES work in the removal case. Indeed, if the run loop deferral is the cause of my problem I would probably have figured it out sooner were it not for this mixed behavior.
Thoughts on what might be occurring there?
(Sent from my iPhone.)
--
Conrad Shultz
On Jul 2, 2012, at 8:23, Michael Babin <email@hidden> wrote:
> On Jul 2, 2012, at 4:35 AM, Conrad Shultz wrote:
>
>> -My model contains an NSMutableArray
>> -An NSArrayController has its content bound to said NSMutableArray and is configured to prepare content (which takes the form of another custom model object)
>> -Said NSMutableArray is mutated only through the safe NSArrayController methods (-add:, -remove:, etc.)
>>
>> Everything works just fine, including undo and redo, which I manage by adding appropriate -prepareWithInvocationTarget: calls in the model's -insertObject:in[PropertyName]AtIndex: and -removeObjectFrom[PropertyName]AtIndex: methods.
>>
>> Now, I want to give the undo actions nice names. To maintain a good MVC pattern I created wrapper action methods in my controller class (not the NSArrayController, but a custom NSViewController subclass that manages a variety of behaviors) such as:
>>
>> - (void)addRecord:(id)sender
>> {
>> if (! [[self undoManager] isUndoing]) {
>> [[self undoManager] setActionName:NSLocalizedString(@"Add Record", nil)];
>> }
>> [[self arrayController] add:sender];
>> }
>>
>> - (void)removeRecord:(id)sender
>> {
>> if (! [[self undoManager] isUndoing]) {
>> [[self undoManager] setActionName:NSLocalizedString(@"Remove Record", nil)];
>> }
>> [[self arrayController] remove:sender];
>> }
>>
>> and have my buttons, menu items, etc., call these wrapper methods.
>>
>> Here's the strange part: the action name appears to only have an effect in the *removal* case, never in the *addition* case.
>>
>> To be clear: everything else works fine. If I perform an action triggering -addRecord:, the record *is* added and a functional undo action *is* registered, just with no action name. But this only happens in the addition case - in the removal case, everything works *including the action name*.
>>
>> (If I move the -setActionName: into the model layer (-inserObject:…) the action name also works.)
>>
>> For debugging, I have verified that the expected code is called in the controller layer, and that the undo manager is both non-nil and is the same undo manager instance as used in the model layer. Indeed, I can even see that the action name has been purportedly set when stepping through the code:
>>
>> (lldb) po [[self undoManager] undoActionName]
>> (id) $1 = 0x00000001045f1520 Add Record
>>
>> But this never appears in the UI. I also set a global breakpoint on [NSUndoManager setActionName:] and can see that it's only getting called once, inside my -addRecord: method. So it doesn't seem to be getting clobbered later on.
>>
>> I have even been able to reproduce this behavior in a minimal test project (mashing together the model and controller layers), which you can download from:
>>
>> https://dl.dropbox.com/u/5847625/BindingsUndoTest.zip
>>
>> So my questions are severalfold:
>>
>> 1) Has anyone else seen this and can explain what's going on? Have I missed something?
>>
>> 2) Can anyone suggest a workaround, preferably short of moving -setActionName: into the model?
>>
>> 3) Is there perhaps a more appropriate way for me to handle undo/redo in this context?
>>
>> This is on OS X, 10.7.4, FYI. I am targeting Lion.
>
> I believe you're not seeing the "Add Record" action name because you're setting the action name for the current undo group in one iteration of the runloop and the action itself (-[NSArrayController add:]) is taking place in another iteration of the runloop (with a different undo group).
>
> https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/UndoArchitecture/Articles/UndoManager.html
>
> "NSUndoManager normally creates undo groups automatically during the run loop. The first time it is asked to record an undo operation in the run loop, it creates a new group. Then, at the end of the loop, it closes the group."
>
> https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSArrayController_Class/Reference/Reference.html
>
> Under "Special Considerations" for add:
>
> "Beginning with Mac OS X v10.4 the result of this method is deferred until the next iteration of the runloop so that the error presentation mechanism (see Error Responders and Error Recovery) can provide feedback as a sheet."
>
>
_______________________________________________
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