Observing the editing property of a UITableViewController
Asked Answered
T

2

11

Why can't I observe the editing property of an instance of UITableViewController?

I'm using the following code:

[self addObserver:self 
       forKeyPath:@"editing" 
          options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
          context:NULL];

And have implemented the method:

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context

... but the observeValueForKeyPath method is never called when this value changes.


According to Apple's Ensuring KVC Compliance section:

For properties that are an attribute or a to-one relationship, this requires that your class:

  • Implement a method named -<key>, -is<Key>, or have an instance variable <key> or _<key>.
  • If the property is mutable, then it should also implement -set<Key>:.
  • Your implementation of the -set<Key>: method should not perform validation.
  • Your class should implement -validate<Key>:error: if validation is appropriate for the key.

The documentation for the editing property, states that it is defined as:

@property(nonatomic, getter=isEditing) BOOL editing

Since this property is not mutable, the only bullet point it must conform to is the first one (i.e. that there is an -is<Key> method defined, for example). You can see that it does conform to this by looking at the declaration of the property, and noticing that there is an isEditing method defined. Thus, it should be Key Value Observing compliant. How come it isn't working?

Typehigh answered 28/9, 2010 at 21:9 Comment(2)
Just out of curiosity, why are you trying to observe the property, as opposed to implementing setEditing:animated:?Quag
@Gordon: for debugging reasons related to that method.Typehigh
R
24

You're confusing Key-Value Coding compliance with Key-Value Observing compliance. The property is KVC-compliant, which means you can use [myViewController valueForKey:@"editing"] to access it (if you like typing), but this does not mean it is KVO-compliant.

KVO-compliance is achieved by the object either implementing a KVC-compliant setter (bullet points 2 and 3), which KVO will wrap automatically, or manually posting KVO notifications by sending itself will/didChangeValueForKey: messages.

UIViewController and UITableViewController do not publicly implement setEditing:; if they don't implement it at all, then KVO wrapping it automatically is out. That leaves manual notifications. If you're not getting any KVO notifications for the property (and you are actually hitting that addObserver:forKeyPath:options:context: message), that suggests that those classes neither privately implement setEditing: nor manually post KVO notifications.

Therefore, the property is not observable.

If the only way anything ever sets the editing property is by sending the controller a setEditing:animated: message, then you can override setEditing:animated: and send the KVO notifications yourself from your implementation, and then the property will be observable.

Reiche answered 28/9, 2010 at 22:10 Comment(1)
Thanks for the informative answer. I have seen that page that you link to, but unfortunately, it doesn't explain it as well as you have.Typehigh
A
0

It's a bit janky but you can work around this by observing the editButtonItem's title.

[self.viewControllerToObserve addObserver:self forKeyPath:@"editButtonItem.title" options:0 context:kMyViewControllerKVOContext];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (context == kMyViewControllerKVOContext) {
        // editing changed
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

FYI this is how I declare my context (above @implementation):

static void * const kMyViewControllerKVOContext = (void *)&kMyViewControllerKVOContext;

Using Hopper, we can see during UIViewController's setEditing it creates a new editButtonItem with a title dependent on what editing is being changed to.

/* @class UIViewController */
-(void)setEditing:(bool)arg2 animated:(bool)arg3 {
    rdx = arg2;
    rdi = self;
    rax = *ivar_offset(_viewControllerFlags);
    rcx = *(rdi + rax);
    if (((rcx & 0x4) >> 0x2 ^ rdx) == 0x1) {
            stack[-8] = rbp;
            stack[-16] = r15;
            stack[-24] = r14;
            stack[-32] = r13;
            stack[-40] = r12;
            stack[-48] = rbx;
            rsp = rsp - 0x38;
            r12 = rdi;
            *(rdi + rax) = (rcx & 0xfffffffffffffffb) + (rdx & 0xff) * 0x4;
            r15 = [UIBarButtonItem alloc];
            r14 = [__UIKitBundle() retain];
            if ((rdx & 0xff) != 0x0) {
                    rax = [r14 localizedStringForKey:@"Done" value:rcx table:@"Localizable"];
                    rax = [rax retain];
                    r13 = rax;
                    rcx = 0x2;
                    rdi = r15;
                    rdx = rax;
            }
            else {
                    rax = [r14 localizedStringForKey:@"Edit" value:rcx table:@"Localizable"];
                    rax = [rax retain];
                    r13 = rax;
                    rdi = r15;
                    rdx = rax;
                    rcx = 0x0;
            }
            rbx = [rdi initWithTitle:rdx style:rcx target:0x0 action:0x0];
            [r13 release];
            [r14 release];
            [r12->_editButtonItem _setItemVariation:rbx];
            [rbx release];
    }
    return;
}

Little more for those interested:

/* @class UIBarButtonItem */
-(void)_setItemVariation:(void *)arg2 {
    rdx = arg2;
    rdi = self;
    rax = *ivar_offset(_barButtonItemFlags);
    if ((*(int8_t *)(rdi + rax) & 0x10) == 0x0) {
            rax = [rdx retain];
            r15 = rax;
            rax = [rax title];
            rax = [rax retain];
            [rdi setTitle:rax];
            [rax release];
            rbx = [r15 style];
            [r15 release];
            [rdi setStyle:rbx];
    }
    return;
}
Ataraxia answered 24/2, 2020 at 17:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.