>> On 3 Feb 2016, at 5:05 PM, Graham Cox <graham....@bigpond.com 
>> <mailto:graham....@bigpond.com>> wrote:
>> 
>> Is there a good way to automate this for a given set of properties?
> 
> 
> BTW, it would be really great if this were an extension of property 
> attributes, e.g:
> 
> @property (nonatomic, assign, refresh) BOOL   goesWild;
> 
> Then the compiler’s synthesis mechanism coould just add the appropriate call 
> for views or layers as it finds. For other classes, this flags a warning.
> 
> Good idea?

Hi Graham.  

I don’t know how to extend language definitions like property attributes, and 
further, I’m not sure such attribute could be bound  to specific code that will 
call setNeedsDisplay on the instance, as language is usually unaware of the 
object types being handled. 

Yet —  your original wish can be easily granted using KVC/KVO protocol, by 
creating dependencies.

If, for example, your NSView subclass has 3 properties whose changes should 
cause the NSView to setNeedsDisplay — e.g.

@property (nonatomic) NSString *dependentPropertyA;
@property (nonatomic) BOOL       dependentPropertyB;
@property (nonatomic) CGColor   dependentPropertyC;

You can do the following in your NSView subclass implementation file.

1. Define a single property (e.g. “BOOL viewShouldRefresh) whose change drives 
the setNeedsDisplay. It will act like a “calculated property” dependent on your 
other properties.

@property (atomic, readonly) BOOL viewShouldRefresh;    // atomic, to prevent 
reentrance if dependent properties change in different threads.

2. Introduce the following KVC class method, to create dependency of 
viewShouldRefresh on the other properties:

+ (NSSet *)keyPathsForValuesAffectingViewShouldRefresh {
        return [NSSet setWithObjects: @“dependentPropertyA”, 
@“dependentPropertyB”, @“dependentPropertyC” ];
}

3. Implement the getter of viewShouldRefresh (not really necessary, but if you 
want to elaborate and add logic that will decide whether or not to refresh 
based on actual state of the triggering properties, you can put the logic here. 
This is the simplest: any change of any dependent property will refresh the 
view.

- (BOOL) viewShouldRefresh:(BOOL) {
        return YES;
}

4. Add KVO observation for the viewShouldRefresh to your NSView subclass (say 
in the -init)

-(instancetype) init {
…
        [self addObserver:self forKeyPath:@“viewShouldRefresh” 
options:NSKeyValueObservingOptionNew |  options:NSKeyValueObservingOptionOld];
}

-(void) dealloc {
        [self removeObserver:self forKeyPath:@“viewShouldRefresh”];
} 

5. Finally - add the KVO generic observation method.

- (void)observeValueForKeyPath:(NSString *)keyPath of object:(id)object 
change:(NSDictionary *)change context:(void *)context {
        if (obj != self || ![keyPath isEqualToString:@“viewShouldRefresh” )     
// ignore KVO of other objects and paths.
                return;

        if      (self.viewShouldRefresh)        // the property will calculate 
the need to refresh — in this sample - always YES.
                [self setNeedsDisplay];
}


That’s it. 

From now on - adding a new dependent property will be as easy as adding its 
name to the NSSet returned by keyPathsForValuesAffectingViewShouldRefresh.

This technique applies of course to CALayers and other things - it’s just 
creating the dependency - not dictating the function.

Motti Shneor.


_______________________________________________

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

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

Reply via email to