How can I test if the scroll view is bouncing? Is there a notification or something when the bounce ends?
Yes... check out the UIScrollViewDelegate spec, implement the methods including the two below, and set your UIScrollView's delegate accordingly:
// User stops dragging the table view.
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
willDecelerate:(BOOL)decelerate;
// Control slows to a halt after the user drags it
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
You will probably be most interested in scrollViewDidEndDecelerating. These also work in UITableView which is where I originally found them (UITableView inherits from UIScrollView).
Here is how I detected if the scroll view is bouncing horizontally:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.contentOffset.x < 0) {
NSLog(@"bouncing left");
}
if (scrollView.contentOffset.x > (scrollView.contentSize.width - scrollView.frame.size.width)) {
NSLog(@"bouncing right");
}
}
I've implemented extension for UIScrollView to handle this for vertical and horizontal scrolling. This will work even with non-zero contentInsets and in case when content is not big enough for covering scrollView insets:
Objective-C
@interface UIScrollView (Bouncing)
@property (nonatomic, readonly) BOOL isBouncing;
@property (nonatomic, readonly) BOOL isBouncingTop;
@property (nonatomic, readonly) BOOL isBouncingLeft;
@property (nonatomic, readonly) BOOL isBouncingBottom;
@property (nonatomic, readonly) BOOL isBouncingRight;
@end
@implementation UIScrollView (Bouncing)
- (BOOL)isBouncing
{
return self.isBouncingTop || self.isBouncingLeft || self.isBouncingBottom || self.isBouncingRight;
}
- (BOOL)isBouncingTop
{
return self.contentOffset.y < - self.contentInset.top;
}
- (BOOL)isBouncingLeft
{
return self.contentOffset.x < - self.contentInset.left;
}
- (BOOL)isBouncingBottom
{
BOOL contentFillsScrollEdges = self.contentSize.height + self.contentInset.top + self.contentInset.bottom >= CGRectGetHeight(self.bounds);
return contentFillsScrollEdges && self.contentOffset.y > self.contentSize.height - CGRectGetHeight(self.bounds) + self.contentInset.bottom;
}
- (BOOL)isBouncingRight
{
BOOL contentFillsScrollEdges = self.contentSize.width + self.contentInset.left + self.contentInset.right >= CGRectGetWidth(self.bounds);
return contentFillsScrollEdges && self.contentOffset.x > self.contentSize.width - CGRectGetWidth(self.bounds) + self.contentInset.right;
}
@end
Swift 3.0+
extension UIScrollView {
var isBouncing: Bool {
return isBouncingTop || isBouncingLeft || isBouncingBottom || isBouncingRight
}
var isBouncingTop: Bool {
return contentOffset.y < -contentInset.top
}
var isBouncingLeft: Bool {
return contentOffset.x < -contentInset.left
}
var isBouncingBottom: Bool {
let contentFillsScrollEdges = contentSize.height + contentInset.top + contentInset.bottom >= bounds.height
return contentFillsScrollEdges && contentOffset.y > contentSize.height - bounds.height + contentInset.bottom
}
var isBouncingRight: Bool {
let contentFillsScrollEdges = contentSize.width + contentInset.left + contentInset.right >= bounds.width
return contentFillsScrollEdges && contentOffset.x > contentSize.width - bounds.width + contentInset.right
}
}
For RxSwift you can just map scrollViewDidScroll
with isBouncing
and filter with distinctUntilChanged
.
A minor amendment to Justin's method, allowing for contentInset:
if( scrollView.contentOffset.x < -scrollView.contentInset.left )
{
NSLog( @"bounce left" );
}
if( scrollView.contentOffset.x > scrollView.contentSize.width - scrollView.frame.size.width + scrollView.contentInset.right )
{
NSLog( @"bounce right" );
}
Yes... check out the UIScrollViewDelegate spec, implement the methods including the two below, and set your UIScrollView's delegate accordingly:
// User stops dragging the table view.
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
willDecelerate:(BOOL)decelerate;
// Control slows to a halt after the user drags it
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
You will probably be most interested in scrollViewDidEndDecelerating. These also work in UITableView which is where I originally found them (UITableView inherits from UIScrollView).
Old question but I just ran into a similar issue and wanted to add that it is a good idea to also check that the scroll views content is larger than the scroll view's frame:
+ (BOOL) isScrollViewBouncing:(UIScrollView *)scrollView
{
return scrollView.contentOffset.y > scrollView.contentSize.height - scrollView.frame.size.height
&& scrollView.contentSize.height > scrollView.frame.size.height;
}
This now makes sure the scroll view is large enough to be in a scroll-bouncing state, so that if the scroll view is small it does NOT always evaluate to true.
Cheers
For those of you who would be able to "bounce" downwards in a scrollview in order to update contents of the view. (And the event is only fired once.)
- (void)scrollViewDidEndDragging:(UIScrollView *)aScrollView
willDecelerate:(BOOL)decelerate{
CGPoint offset = aScrollView.contentOffset;
CGRect bounds = aScrollView.bounds;
CGSize size = aScrollView.contentSize;
UIEdgeInsets inset = aScrollView.contentInset;
float y = offset.y + bounds.size.height - inset.bottom;
float h = size.height;
//Distance in points before update-event occur
float reload_distance = 50;
//
if(y > h + reload_distance) {
NSLog(@"load more rows");
}
}
Using Glavid's answer, I added bouncing to bottom also, and added as a category
#import "UIScrollView+Additions.h"
@implementation UIScrollView (Additions)
- (BOOL)isBouncing
{
BOOL isBouncingBottom = self.contentOffset.y >= self.contentSize.height - self.frame.size.height
&& self.contentSize.height >= self.frame.size.height;
BOOL isBouncingTop = self.contentOffset.y <= 0;
BOOL isBouncing = isBouncingBottom || isBouncingTop;
return isBouncing;
}
@end
© 2022 - 2024 — McMap. All rights reserved.