3
votes

I'm inserting NSTableView in menubar popup.

I want to resize my popup's panel to fit tableview with its contents. For example, if there are no rows - tableview is hidden. With adding new rows I am recalculating height of panel based on current tableview height + height of new cell.

I'm adding cells with

[self.tableView insertRowsAtIndexes:[NSIndexSet indexSetWithIndex:0] withAnimation:NSTableViewAnimationSlideDown];

and updating panel's height with:

[[self window] setFrame:panelRect display:YES animate:YES];

(all in the same addRow method)

That's working fine, but looks ugly - tableview and window are animating separately of each other. All I want - is to sync them, to move border of the window simultaneously with tableview growing and shrinking.

Based on my iOS development experience, I've had an idea - to observe tableview's contentSize, but I failed in that approach:

- (void)awakeFromNib
{
    [super awakeFromNib];
    ...
    [self.scrollView.documentView addObserver:self forKeyPath:@"bounds.size" options:NSKeyValueObservingOptionNew context:NULL];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    NSLog(@"!");
}

self.scrollview is linked to tableview's container scrollview in IB. That code leads to exception:

An instance 0x10053a1d0 of class NSConcreteValue was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x10053c850> (
<NSKeyValueObservance 0x10053c7a0: Observer: 0x10053a160, Key path: size, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x10053c880>
)

Using frame.size keypath leads to the same exception. Using just frame returns no callback.

Would be grateful for any leads to my error in my KVO or some another idea of implementing tableview with window resize feature.

2

2 Answers

3
votes

Might not solve your problem with animating. But you should remove your observer from the object before the object is deallocated. That would make the exception go away. i.e in this case you should

[self.scrollView.documentView removeObserver:self forKeyPath:@"bounds.size"];

[self.scrollView.documentView removeObserver:self forKeyPath:@"frame"]

This should be done before self.scrollView.documentView goes out of memory. Considering that can be tricky, you could try adding observer like this:

[self addObserver:self forKeyPath:@"self.scrollView.documentView.bounds.size" options:NSKeyValueObservingOptionNew context:Nil];

[self addObserver:self forKeyPath:@"self.scrollView.documentView.frame" options:NSKeyValueObservingOptionNew context:Nil];

Another thing, i remember reading somewhere is, observing the frame or bounds, is generally not considered a good Idea, but I'm not sure about this.

EDIT: Considering other answer: "bounds.size" won't work since since size is not set directly using any setter method. So instead, observe the frame and extract the size information from it. (For UIView only frame is observable and not bounds. I guess the same applies for NSView)

2
votes
  1. bounds and frame are C type structure, not object.
  2. Although Cocoa wraps bounds and frame with NSValue automatically, NSRect does not wrap NSSize.
  3. The change notification would be triggered by setXXX function. Try add a button, and write action as following:

    [self.scrollView.documentView setBounds:NSMakeRect(0, 0, 100, 100)];