3

I need to know when the frame of any NSSplitView subviews have changed, but only after they have finished resizing.

Currently I am using this in an NSSplitView subclass:

[[NSNotificationCenter defaultCenter] addObserver: self
                                      selector: @selector(didResize:)
                                      name: NSSplitViewDidResizeSubviewsNotification
                                      object: self];

But the problem I have is that this sends hundreds of notifications as the split views are being resized or the containing window changes it's frame size. This adversely effects performance a lot!

How can I tell once the split views have changed frame for good (without adding any overhead or messyness - a timer checking every so often if resizing has stopped isn't really the solution I want to go for).

Jordan Smith
  • 10,310
  • 7
  • 68
  • 114
  • Unfortunately, there is no method that does what you want, so I think, doing something like I have in my answer is as good as you're going to get. This doesn't really add a lot of overhead or messiness - you wouldn't be doing anything else while you're resizing anyway, so multiple calls to that delegate method doesn't degrade the app's performance, as long as what you do in that method is minimal. – rdelmar May 18 '13 at 03:49

4 Answers4

1

I think you need to use some sort of timing method if you don't want to be responding to each change. Something like this should work pretty well I think. The first line will cancel out the second one, as long as the method is called again within .1 seconds.

-(void)splitViewDidResizeSubviews:(NSNotification *)notification {
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(respondToSubviewChange) object:nil];
    [self performSelector:@selector(respondToSubviewChange) withObject:nil afterDelay:.1];
}

-(void)respondToSubviewChange {
    NSLog(@"here");
   // Do your work here.
}

BTW, if the class that this code is in is the delegate of the split view, then you don't need to register for this notification, it's automatically registered.

After Edit:

I did find another way that doesn't use any timing mechanism, but I don't know how robust it is. It relies on the fact that splitView:constrainMinCoordinate:ofSubviewAt: is called when you mouseDown in the divider, and again with mouseUp. It's also called once when the app first starts up (or maybe when the window the split view is in, is loaded, or something -- I didn't test it with more than one window). So, setting "timesCalled" to -1 (instead of 0) is to get the logic to ignore that first call when the app starts. Thereafter, the if clause evaluates to true on every other call (which would be on mouseUp) to the delegate method.

- (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMin ofSubviewAt:(NSInteger)dividerIndex {
    static int timesCalled = -1;
    if (timesCalled % 2 == 1) {
        NSLog(@"Do stuff");
        // Do your work here.
    }
    timesCalled ++;
    return 0 // 0 allows you to minimize the subview all the way to 0;
}
rdelmar
  • 103,982
  • 12
  • 207
  • 218
0

You could look for mouseDown mouseDragged and mouseUp events from within the pane splitter in a subclass. At that point you could use that to post a notification from the subclass. You might check existing NSSplitView subclasses like the venerable RBSplitView and see if they already do what you want.

uchuugaka
  • 12,679
  • 6
  • 37
  • 55
0

Implement this delegate method only. When i implemented other resizing delegate methods, the results are messed up.

This method is called everytime the mouse moves and once again when the mouse is released.

-(void)splitViewDidResizeSubviews:(NSNotification *)notification{

if (notification.object == self.contentSplitView){ //get the right splitview

    static CGFloat last = 0; //keep tracks with widths
    static BOOL didChangeWidth = NO; //used to ignore height changes

    CGFloat width_new = self.tableview_images.frame.size.width; //get the new width
    if (last == width_new && didChangeWidth){ //
        [self.tableview_images reloadData];

        NSLog(@"finish!");
        didChangeWidth = NO;

    } else {


        if (last != width_new){
            if (last != 0){
                didChangeWidth = YES;
            }
        }
    }
    last = width_new;

}

}

ssj
  • 881
  • 1
  • 10
  • 21
0

viewDidEndLiveResize is called on NSSplitView after a window resize and for divider drags. You can send a custom notification using that.

extension Notification.Name {
    static public let didEndLiveResize = Notification.Name("didEndLiveReze")
}

class SplitView: NSSplitView
{
    override func viewDidEndLiveResize() {
        super.viewDidEndLiveResize()
        NotificationCenter.default.post(name: .didEndLiveResize, object: self)
    }
}
Giles
  • 1,428
  • 11
  • 21