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;
}
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.
if (_scrollView.verticalScroller.floatValue > 0.9)
{
// bottom
// do something
}
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")
}
}
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.