Reputation: 5534
I've been searching for a way to disable vertical bounce for a UIScrollView but only at bottom. I still need to have the bounce at the top.
Couldn't find anything. Is this possible?
Thanks in advance!
Upvotes: 28
Views: 28763
Reputation: 2852
To disable vertical bounce at the top in Swift:
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentOffset.y < 0 {
scrollView.contentOffset.y = 0
}
}
To disable vertical bounce at the bottom in Swift:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y > scrollView.contentSize.height - scrollView.bounds.height {
scrollView.contentOffset.y = scrollView.contentSize.height - scrollView.bounds.height
}
}
Upvotes: 15
Reputation: 9276
Let's disable the bounces
when scroll to the bottom.
scrollView.bounces = scrollView.contentOffset.y < scrollView.contentSize.height - scrollView.frame.height
Upvotes: 1
Reputation: 89
I have been looking a solution to disable bottom bounce only for a scrollview with wider contentSize width than device since I am using paging. None of those work for me but I found a way to do it. I thought I should share this since I wasted two days just to do this. I am using Swift 4 but this applies to objective c if you know how to convert this which should be easy if you have experience with both.
This works in a scrollview with device screen frame as frame.
Add variable to be aware for scrollview's current page.
var currentPage : Int!
Set initial value
currentPage = 0
Update currentPage
after scrolling
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let width = scrollView.frame.size.width
let page = (scrollView.contentOffset.x + (0.5 * width)) / width
currentPage = Int(page)
}
Filter bottom scrolling and update contentOffset
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if CGFloat(currentPage) * screenWidth < scrollView.contentOffset.x || CGFloat(currentPage) * screenWidth < scrollView.contentOffset.y || (((CGFloat(currentPage) * screenWidth - scrollView.contentOffset.x) >= 0) && scrollView.contentOffset.y > 0){
scrollView.contentOffset.y = 0
}
}
CGFloat(currentPage) * screenWidth < scrollView.contentOffset.x || CGFloat(currentPage) * screenWidth < scrollView.contentOffset.y
will filter bottom left and right scrolling
(((CGFloat(currentPage) * screenWidth - scrollView.contentOffset.x) >= 0) && scrollView.contentOffset.y > 0)
will filter bottom mid scrolling
scrollView.contentOffset.y = 0
will stop it from scrolling
Upvotes: 0
Reputation: 41
Swift 3:
This works to disable vertical bounce only at the bottom:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y > scrollView.contentSize.height - scrollView.bounds.height {
scrollView.contentOffset.y = scrollView.contentSize.height - scrollView.bounds.height
}
}
Upvotes: 4
Reputation: 394
I added fabsf()
to 0x7fffffff♦'s solution to also cater for negative(downward) scroll:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (fabsf(scrollView.contentOffset.y) >= scrollView.contentSize.height - scrollView.frame.size.height) {
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, scrollView.contentSize.height - scrollView.frame.size.height)];
}
}
Upvotes: 0
Reputation: 5534
Based on 0x7fffffff's answer I've created a class:
VerticalBounceControl.h
#import <UIKit/UIKit.h>
@interface VerticalBounceControl : NSObject
@property (nonatomic, assign) BOOL bounce;
- (id) initWithScrollView:(UIScrollView*)scrollView bounceAtTop:(BOOL)bounceAtTop bounceAtBottom:(BOOL)bounceAtBottom;
@end
VerticalBounceControl.m
#import "VerticalBounceControl.h"
@interface VerticalBounceControl ()
@property (nonatomic, retain) UIScrollView* scrollView;
@property (nonatomic, assign) BOOL bounceAtTop;
@property (nonatomic, assign) BOOL bounceAtBottom;
@end
@implementation VerticalBounceControl
- (id) initWithScrollView:(UIScrollView*)scrollView bounceAtTop:(BOOL)bounceAtTop bounceAtBottom:(BOOL)bounceAtBottom {
self = [super init];
if(self) {
self.bounce = YES;
self.scrollView = scrollView;
self.bounceAtTop = bounceAtTop;
self.bounceAtBottom = bounceAtBottom;
[self.scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:NULL];
}
return self;
}
- (void) dealloc {
[self.scrollView removeObserver:self forKeyPath:@"contentOffset"];
self.scrollView = nil;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:@"contentOffset"]) {
if (self.bounce && ((self.scrollView.contentOffset.y<=0 && self.bounceAtTop) || (self.scrollView.contentOffset.y>=self.scrollView.contentSize.height-1 && self.bounceAtBottom))) {
self.scrollView.bounces = YES;
} else {
self.scrollView.bounces = NO;
}
}
}
@end
that can be used without the caller being a delegate of UIScrollView
like this:
VerticalBounceControl* bounceControl = [[VerticalBounceControl alloc] initWithScrollView:anyScrollView bounceAtTop:YES bounceAtBottom:NO];
This way you can have this bounce behavior for UIScrollView
s that you don't want to alter their delegate (like UIWebView
's one).
Again, thanks go @0x7fffffff for the solution!
Upvotes: 4
Reputation: 130193
Use the UIScrollViewDelegate method scrollViewDidScroll
to check the content offset of the scrollview and more or less check if the user is trying to scroll beyond the bottom bounds of the scroll view. Then just use this to set the scroll back to the end of the scroll view. It happens so rapidly that you can't even tell that the scroll view bounced or extended beyond its bounds at all.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.frame.size.height) {
[scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, scrollView.contentSize.height - scrollView.frame.size.height)];
}
}
Upvotes: 57