sqreept
sqreept

Reputation: 5534

UIScrollView disable vertical bounce only at bottom

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

Answers (7)

efremidze
efremidze

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

Tai Le
Tai Le

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

726a
726a

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.

  1. Add variable to be aware for scrollview's current page.
    var currentPage : Int!

  2. Set initial value
    currentPage = 0

  3. 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) }

  4. 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

Jordi Tambillo
Jordi Tambillo

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

MickeDG
MickeDG

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

sqreept
sqreept

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 UIScrollViews that you don't want to alter their delegate (like UIWebView's one).

Again, thanks go @0x7fffffff for the solution!

Upvotes: 4

Mick MacCallum
Mick MacCallum

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

Related Questions