Home > Mobile >  KVO check for change of clipsToBounds of all subviews in an UIView in objective c
KVO check for change of clipsToBounds of all subviews in an UIView in objective c

Time:12-27

I am trying to implement a KVO example for clipsToBounds property of all subviews in my UIView. I do not quite understand how to change the value in observeValueForKeyPath method. I am using this code:

-(void)ViewDidLoad{
        [self.navigationController.view addObserver:self forKeyPath:@"clipsToBounds" options:NSKeyValueObservingOptionNew |
         NSKeyValueObservingOptionOld context:nil];
    }
    
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    NSLog(@"Triggered...")
    }

It is triggered when ever i change the property clipToBounds of a subview that exists in the UIView i have. I need to change the value back to false for every trigger that happens. What should i write inside the observeValueForKeyPath to change the clipsToBounds property? Any help appreciated.

CodePudding user response:

of course adding the Observer must be done before it works. Guessing your typo in "ViewDidLoad" would just never be called because it should be "viewDidLoad". Apart from that your KVO pattern could look like..

static void *kvoHelperClipsToBounds = &kvoHelperClipsToBounds;

-(void)viewDidLoad {
    [self.navigationController.view addObserver:self forKeyPath:@"clipsToBounds" options:NSKeyValueObservingOptionNew context:&kvoHelperClipsToBounds];
}
    
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (context == kvoHelperClipsToBounds) {
        NSLog(@"context compared successful...");
        //be careful what you cast to.. i dont check isKindOf here.
        UINavigationBar* navbar = (UINavigationBar*)object;
        if (navbar.subviews.count > 1) {
            __kindof UIView *sub = navbar.subviews[1];
            if (sub.clipsToBounds) {
                dispatch_async(dispatch_get_main_queue(),^{
                    sub.clipsToBounds = NO;
                    [self.navigationItem.titleView layoutIfNeeded];
                });
            }
        }
    } 

    // or compare against the keyPath
    else if ([keyPath isEqualToString:@"clipsToBounds"]) {
        NSLog(@"classic Key compare Triggered...");
    }
    else
    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}

[super observeValueForKeyPath...] passes not recognized keyPath to super to let super's class KVO work, otherwise those would be ignored if super's implementation would rely on observing them. Which should also explain how you could observe all subviews if needed. But think about that there could be potentially hundrets of subviews triggering observeValueForKeyPath if a subclass of UIView would implement it and all subviews (or the ones you like) would be inherited also from this special subclass.

When you change clipsToBounds inside the KVO where you observe it, you possibly invoke a loop, specially when you watch both - old and new values. you would change the property, the property triggers kvo, kvo changes the property, the property triggers kvo and on and on.

set [self.navigationController.view setClipsToBounds:YES] to change the property. But if done inside KVO it will trigger KVO again as explained. Usually you would set clipsToBounds in -initWithFrame: or in -initWithCoder: or via Interface Builder and maybe just observe if it gets changed to adapt some other code.

Sidenote: the context just needs to be unique to distinguish it from other KVO.. it could also be reference to a real objects pointer.

Don't forget added Observers must be removed before deallocation.

  • Related