On 26 Dec 2011, at 02:44, Quincey Morris wrote:

> On Dec 25, 2011, at 14:05 , Luc Van Bogaert wrote:
> 
>> I'm adding a custom user setting to a NSPrintPanel by adding an "accessory 
>> view controller" for a view that contains just one control (a checkbox) and 
>> which is loaded from a nib file. The checkbox's value has a binding to the 
>> view controller's "representedObject.printSettings.checkboxvalue" keypath. 
>> (The representedObject is a NSPrintInfo object).
>> 
>> I have been able to verify that this binding works correctly.
>> 
>> One thing I haven't gotten to work yet, is to automatically update the 
>> "preview" in the print panel when I chance the state of the checkbox. The 
>> preview updates itself correctly when I change one of the "built-in" 
>> settings, and it even correctly represents my own custom setting. But, how 
>> can I trigger an automatic update when I just change my own custom setting? 
>> 
>> I assume I should implement the  "keyPathsForValuesAffectingPreview" 
>> protocol method in my accessory view controller, which I did, but so far 
>> without success. Here's my implementation:
>> 
>> - (NSSet *)keyPathsForValuesAffectingPreview
>> {
>>    return [NSSet setWithObjects:
>>            [NSString 
>> stringWithFormat:@"representedObject.printSettings.checkboxvalue"],
>>            nil];
>> }
> 
> The immediate problem is that binding through a mutable dictionary like this 
> (printSettings) isn't KVO compliant. Properties with values that are mutable 
> objects are generally a problem for KVC/KVO, since it's really designed 
> around the idea that property values are immutable objects like NSString and 
> NSNumber.
> 
> The secondary problem is that you're trying, simultaneously, to use 2 
> different ways to get the accessory panel to update automatically, and both 
> ways aren't working. If your binding was working, then 
> 'keyPathsForValuesAffectingPreview' wouldn't be necessary. If you had a 
> working 'keyPathsForValuesAffectingPreview', then I suspect you'd find that 
> the checkbox still wouldn't change -- not because it wasn't updating, but 
> because the change wouldn't be propagated.
> 
> I think the simplest approach is to add a "checkboxvalue" property to the 
> view controller, bind the checkbox to File's Owner.checkboxvalue, and then 
> make this property KVO-compliant:
> 
> 1. The getter can just return 
> self.representedObject.printSettings.checkboxvalue.
> 
> 2. The setter can just change 
> self.representedObject.printSettings.checkboxvalue.
> 
> 3. The view controller needs to keep track of the current print settings 
> object by observing its own "representedObject.printSettings" keypath.
> 
> 4. Whenever the print settings object changes, the view controller needs to 
> observe the "checkboxvalue" key of the new object and stop observing the same 
> key of the old object.
> 
> 5. Whenever the observed "checkboxvalue" changes, transfer the new value to 
> self.checkboxvalue. Note that this starts a messaging loop between the 2 
> "checkboxvalue" properties, but the KVO machinery prevents this from being an 
> infinite loop. You can short circuit the remaining minor duplication of 
> effort, if you wish, by having your setter check the dictionary property 
> value to avoid re-setting it to the existing value.
> 
> 

I'm afraid I still haven't got this nailed down completely. Please try to 
follow the below explanations of what I've been doing, and correct me where 
nesseccary. Thanks!

At this point, I have only implemented the first two points of your above list. 
Using the checkbox in my accessory view, I can now have the print panel preview 
update itself according the checkbox state, so that's working fine. However, ...

I haven't been able to set the checkbox's state to ON, when opening the print 
panel. I've tried creating the view controller first and then sending 
[viewcontroller setCheckBoxValue:YES], but that doesn't seem to work, so I 
suspect I'm still missing some important stuff (I assume point 3 and 
following..)

These are my current accessor methods:

- (BOOL)checkBoxValue
{
    return [[[[self representedObject] printSettings] 
valueForKey:kCheckBoxValue] boolValue];
}

- (void)setCheckBoxValue:(BOOL)checkBoxValue
{
    [[[self representedObject] printSettings] setValue:[NSNumber 
numberWithBool:checkBoxValue] 
                                                forKey:kCheckBoxValue];
}


** Point 3: why exactly would this be required?

I assume I could add this in -awakefromnib:

    [self addObserver:self 
           forKeyPath:@"representedObject.printSettings" 
              options:NSKeyValueObservingOptionOld | 
NSKeyValueObservingOptionNew 
              context:self];

and implement this in the view controller :

- (void)observeValueForKeyPath:(NSString *)keyPath 
                      ofObject:(id)object 
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if ([keyPath isEqualToString:@"representedObject.printSettings"]) {
        NSLog(@"%@", @"the printSettings object has changed");
    }
}

But, I'm not sure where to remove 'self' as an observer. I'm doubting that 
-dealloc is the right place for thisn but for now I have implemented it there:  

    [self removeObserver:self 
              forKeyPath:@"representedObject.printSettings"];



** Point 4: this is even getting a little harder for me…

Here's what I would do:

    if ([keyPath isEqualToString:@"representedObject.printSettings"]) {
        id oldSettings = [change valueForKey:NSKeyValueChangeOldKey];
        id newSettings = [change valueForKey:NSKeyValueChangeNewKey];
        [self observePrintSettingsWith:newSettings 
                           oldSettings:oldSettings];
    }

and:

- (void)observePrintSettingsWith:(NSMutableDictionary *)newSettings
                     oldSettings:(NSMutableDictionary *)oldSettings
{
    if (newSettings != [NSNull null]) {
        [newSettings addObserver:self 
                      forKeyPath:kCheckBoxValue 
                         options:0 
                         context:self];
    }
    
    if (oldSettings != [NSNull null]) {
        [oldSettings removeObserver:self 
                         forKeyPath:kCheckBoxValue];
    }
}

Where should I remove "self" as an observer for the "first" print settings 
object? Also in -dealloc ?


** Point 5: now I'm definitely loosing track ;-)

When clicking the checkbox, I never see the below text in the log.

    if ([keyPath isEqualToString:@"representedObject.printSettings"]) {
        id oldSettings = [change valueForKey:NSKeyValueChangeOldKey];
        id newSettings = [change valueForKey:NSKeyValueChangeNewKey];
        [self observePrintSettingsWith:newSettings 
                           oldSettings:oldSettings];

    } else if ([keyPath isEqualToString:kPrintDotsUsingSketchDotColor]) {
        NSLog(@"checkbox changed");
    }

To fix this, I have added the following in the setter:

    [[[self representedObject] printSettings] 
willChangeValueForKey:kCheckBoxValue];
    [[[self representedObject] printSettings] setValue:[NSNumber 
numberWithBool:checkBoxValue] 
                                                forKey:kCheckBoxValue];
    [[[self representedObject] printSettings] 
didChangeValueForKey:kCheckBoxValue];

This seems to work.

Finally, I added this in -observeValueForKeyPath :

    } else if ([keyPath isEqualToString:kPrintDotsUsingSketchDotColor]) {
        BOOL newValue = [[change valueForKey:NSKeyValueChangeNewKey] boolValue];
        [self setPrintDotsUsingSketchColor:newValue];
    }

and in the setter:

    if (self.checkBoxValue == checkBoxValue)
        return;


Everything still seems to work just like before, so I haven't gained anything 
from all this so it seems, because I'm still unable to set the checkbox state 
to ON, when opening the print panel. So I'm wondering what I'm still missing?

Any help much appreciated.

-- 
Luc Van Bogaert
http://users.skynet.be/luc.van.bogaert



_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to