Re: Easier way to make NSView subclasses refresh on a property change?
Re: Easier way to make NSView subclasses refresh on a property change?
- Subject: Re: Easier way to make NSView subclasses refresh on a property change?
- From: Motti Shneor <email@hidden>
- Date: Thu, 04 Feb 2016 18:55:32 +0200
>> On 3 Feb 2016, at 5:05 PM, Graham Cox <email@hidden <mailto:email@hidden>> 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 (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