UIScrollView 仅在底部禁用垂直反弹

Posted

技术标签:

【中文标题】UIScrollView 仅在底部禁用垂直反弹【英文标题】:UIScrollView disable vertical bounce only at bottom 【发布时间】:2012-12-29 18:26:16 【问题描述】:

我一直在寻找一种方法来禁用 UIScrollView 的垂直反弹,但仅限于底部。我仍然需要在顶部反弹。

找不到任何东西。这可能吗?

提前致谢!

【问题讨论】:

相关:Disable UIScrollView bounce at the top of the screen,通过根据滚动位置更改 scrollViewDidScroll 中滚动视图的 bounces 属性。 【参考方案1】:

使用 UIScrollViewDelegate 方法scrollViewDidScroll 来检查滚动视图的内容偏移,或多或少地检查用户是否试图滚动超出滚动视图的底部边界。然后只需使用它来将滚动设置回滚动视图的末尾。它发生得如此之快,以至于您甚至都无法判断滚动视图是弹跳还是超出了其边界。

- (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)];
    

【讨论】:

反其道而行之呢?底部反弹? 这样好吗? if (scrollView.contentOffset.y 您可能还需要考虑 contentInset:正确的 scrollView.contentInset.bottom。【参考方案2】:

在 Swift 中禁用顶部的垂直反弹:

func scrollViewDidScroll(scrollView: UIScrollView) 
    if scrollView.contentOffset.y < 0 
        scrollView.contentOffset.y = 0
    

在 Swift 中禁用底部的垂直反弹:

func scrollViewDidScroll(_ scrollView: UIScrollView) 
    if scrollView.contentOffset.y > scrollView.contentSize.height - scrollView.bounds.height 
        scrollView.contentOffset.y = scrollView.contentSize.height - scrollView.bounds.height
    

【讨论】:

@Blacky 这将禁用顶部和底部的反弹,问题只要求一个 如果你还没有设置,只是友情提示添加 UIScrollViewDelegate 和 scrollView.delegate = self【参考方案3】:

根据 0x7fffffff 的回答,我创建了一个类:

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

可以在调用者不是UIScrollView 的代表的情况下使用,如下所示:

VerticalBounceControl* bounceControl = [[VerticalBounceControl alloc] initWithScrollView:anyScrollView bounceAtTop:YES bounceAtBottom:NO];

这样您就可以对不想更改其委托的UIScrollViews 进行这种反弹行为(例如UIWebView 的委托)。

再次感谢 @0x7fffffff 的解决方案!

【讨论】:

UIScrollView 的类别会是一个更优雅的解决方案 @MarkE 快速实施会涉及什么?如果不是没有覆盖委托方法。好奇。 @Pavan 我对 swift 不是很熟悉,我已经有一段时间没有使用 ios dev 了。虽然我的评论与 swift 无关,但类别只是扩展类的一种方式,我不知道 swift 是否可用【参考方案4】:

斯威夫特 3:

这只能在底部禁用垂直反弹:

func scrollViewDidScroll(_ scrollView: UIScrollView) 
    if scrollView.contentOffset.y > scrollView.contentSize.height - scrollView.bounds.height 
        scrollView.contentOffset.y = scrollView.contentSize.height - scrollView.bounds.height
    

【讨论】:

@Jordi 它有效,但有什么办法也可以左右吗?我只需要***邦斯。【参考方案5】:

让我们在滚动到底部时禁用bounces

scrollView.bounces = scrollView.contentOffset.y < scrollView.contentSize.height - scrollView.frame.height

【讨论】:

【参考方案6】:

我在 0x7fffffff♦ 的解决方案中添加了 fabsf() 以同时满足负(向下)滚动:

- (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)];
    

【讨论】:

【参考方案7】:

自从我使用分页以来,我一直在寻找一种解决方案,仅针对 contentSize 宽度比设备更宽的滚动视图禁用底部反弹。这些都不适合我,但我找到了一种方法。我想我应该分享这个,因为我浪费了两天时间来做这个。 我使用的是 Swift 4,但如果你知道如何转换它,这适用于目标 c,如果你有这两种方法的经验,这应该很容易。

这适用于以设备屏幕框架为框架的滚动视图。

    添加变量以了解滚动视图的当前页面。var currentPage : Int!

    设置初始值currentPage = 0

    滚动后更新currentPagepublic func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) let width = scrollView.frame.size.width let page = (scrollView.contentOffset.x + (0.5 * width)) / width currentPage = Int(page)

    过滤底部滚动并更新contentOffsetpublic 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 &lt; scrollView.contentOffset.x || CGFloat(currentPage) * screenWidth &lt; scrollView.contentOffset.y会过滤底部左右滚动

(((CGFloat(currentPage) * screenWidth - scrollView.contentOffset.x) &gt;= 0) &amp;&amp; scrollView.contentOffset.y &gt; 0) 将过滤底部中间滚动

scrollView.contentOffset.y = 0 会阻止它滚动

【讨论】:

以上是关于UIScrollView 仅在底部禁用垂直反弹的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 UIScrollView 和 pagingEnabled=YES 禁用反弹

使用 UIRefreshControl 在 UITableView 中禁用底部反弹

如何仅在正文上启用反弹过度滚动(而不是页眉和页脚)?

滚动时禁用 UITableView 垂直反弹

在滚动时从表格视图中删除反弹效果

快速禁用 UIScrollView 中的垂直滚动?