-->

NSScrollView detect scroll position

2020-07-23 06:57发布

问题:

How can i detect scroll of position when the scroll at bottom?

[[_scrollView contentView] setPostsBoundsChangedNotifications:YES];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(boundsDidChangeNotification:)
                                             name:NSViewBoundsDidChangeNotification
                                           object:[_scrollView contentView]];





- (void) boundsDidChangeNotification: (NSNotification *) notification
{
    NSPoint currentScrollPosition = [[_scrollView contentView] bounds].origin;
}

回答1:

Update:
My original answer was plain wrong.
Thanks to JWWalker and Wil Shipley for making me aware of that via comments.

Here's a hopefully more helpful answer for people coming here via search:
Unlike UIScrollView, NSScrollView does not provide a delegate method to inform you when a view was scrolled to top/bottom.
To detect those situations, you have to enable boundsDidChange notifications and subscribe to them.

When receiving a bounds update, you can check if the y coordinate of the clip view bounds is 0 (= bottom), or if the top edge of the clip view bounds aligns with the document view (= top).

private func configureScrollView() {
        self.scrollView.contentView.postsBoundsChangedNotifications = true
        NotificationCenter.default.addObserver(self, selector: #selector(contentViewDidChangeBounds), name: NSView.boundsDidChangeNotification, object: self.scrollView.contentView)
    }

@objc
func contentViewDidChangeBounds(_ notification: Notification) {
    guard let documentView = scrollView.documentView else { return }

    let clipView = scrollView.contentView
    if clipView.bounds.origin.y == 0 {
        print("bottom")
    } else if clipView.bounds.origin.y + clipView.bounds.height == documentView.bounds.height {
        print("top")
    }
}

For scroll views that use elastic scrolling, the updates come with a short delay because the clip view seems to defer the bounds change notifications until the scroll bounce has finished.

You can use the visible rect of your NSScrollView's contentView instead of the bounds:

- (void)boundsDidChangeNotification:(NSNotification*) notification
{
    NSRect visibleRect = [[_scrollView contentView] documentVisibleRect];
    NSLog(@"Visible rect:%@", NSStringFromRect(visibleRect));
    NSPoint currentScrollPosition = visibleRect.origin;
}

The content view bounds don't change during scrolling, so in your original code, the bounds.origin probably always returns 0/0.



回答2:

if (_scrollView.verticalScroller.floatValue > 0.9)
{
    // bottom
    // do something
}


回答3:

if you want to detect after scroll ended. @thomas's answer is detect when view is scrolling

  NotificationCenter.default.addObserver(
                self,
                selector: #selector(scrollViewDidScroll),
                name:NSScrollView.didLiveScrollNotification,
                object: scrollView
            )



  @objc func scrollViewDidScroll (notification: NSNotification) {

        guard let documentView = scrollView.documentView else { return }

        let clipView = scrollView.contentView
        if clipView.bounds.origin.y == 0 {
            print("top")

        } else if clipView.bounds.origin.y + clipView.bounds.height == documentView.bounds.height {

            print("bottom")

}
    }


回答4:

In the actual language OP requested:

- (void) awakeFromNib
{
    [super awakeFromNib];

    NSView* objectToTrack = self.tableView.enclosingScrollView.contentView;

    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(didObserveScrolling:)
                                                 name: NSViewBoundsDidChangeNotification
                                               object: objectToTrack];
}

- (void) didObserveScrolling: (id) sender
{
    self.needsDisplay = YES;
    //or do whatever you need to in response for the content scrolling

}

- (void) dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver: self];
}

I use this in a custom view that acts as a NSTableView footer to display column averages.