在UIScrollView中限制可滚动区域

8
我有一个UIScrollView,它在滚动一个相当大的UIView。
在某些时候,我想限制用户可以滚动的区域。例如,我可能只想让他们查看视图的底部四分之一。
我可以通过覆盖scrollViewDidScroll并调用setContentOffset来限制区域,如果视图滚动太远。但是这种方式不能像UIScrollView自然滚动超出UIView边界时那样平稳地弹回。
是否有更好的方法来限制UIScrollView中可滚动的区域?
4个回答

18

我会将滚动视图的contentSize属性更改为您希望用户能够滚动的区域大小,并调整子视图的frame.origin,使您想要出现的左上边界相对于滚动视图出现在(0, 0)位置。例如,如果您的视图高度为800个点,而您想显示底部的四分之一,则将contentSize的高度设置为200,并将view.frame.origin的y分量设置为-600。


3
你的回答正是我解决了类似问题所需要的关键。基本上,uiscrollview.frame.size和uiscrollview.contentsize之间的差异就成为了你允许滚动的部分。你可以随意操作放置在uiscrollview中的uiview,使用uiview.frame和uiview.size来获得所需的效果。一旦你理解了这一点,生活就美好了。愉快的滚动吧! :) - HM1
这是一个封装类似逻辑的解决方案的示例: https://github.com/ealeksandrov/EARestrictedScrollView - Evgeny Aleksandrov

7

我找到了一个适合我的解决方案。它允许你滚动到点0,0,但不会超过这个范围:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView.contentOffset.x <= -1) {
        [scrollView setScrollEnabled:NO];
        [self.scrollView setContentOffset:CGPointMake(0, 0) animated:YES];
        [scrollView setScrollEnabled:YES];
    }
}

您可以对上、下或右(x或y)执行相同的操作。

2
这种方法的问题在于你会失去“反弹”的效果——当你滚动超出区域并松开手时,滚动会轻轻地回弹。 - Mongus Pong
1
糟糕,你说得对,我没有仔细阅读。不过,也许这可以帮助其他人。 - Yoko

2
在Swift 4中对Yoko的回答进行小幅改进,将是:
override func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
    if scrollView.contentOffset.y > 600 {
        let anim = UIViewPropertyAnimator(duration: 1, dampingRatio: 0.5) {
            scrollView.isScrollEnabled = false
            scrollView.setContentOffset(CGPoint(x: 0, y: 600), animated: false)
            scrollView.isScrollEnabled = true
        }
        anim.startAnimation()
    }
}

这将使滚动视图的动画效果与预期非常相似。当您处于“弹跳”区域时,较慢的拖动将无法工作,并且动画持续时间必须取决于距离(而不是像这里一样恒定),如果您想要精确控制。您还可以尝试在 scrollViewDidScroll 中执行此逻辑,看看它如何不同。关键是 setContentOffset(_:,animated:) 必须带有 animated: false,以便 UIViewPropertyAnimator 的块可以捕获并对其进行动画处理。


0
另一种方法是重写UIScrollView的方法:- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
返回YES将允许用户滚动。返回NO则不会。
注意:这将禁用所有嵌入在UIScrollView中且pointInside返回NO的视图的所有触摸操作。如果您不希望滚动的区域没有任何交互,则此方法非常有用。

这个例子只允许用户在滚动UITableView时才能滚动UIScrollView。(一个UITableView和两个UIViews嵌入在UIScrollView中)

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    for (UIView *subview in self.subviews) {
        if ([subview pointInside:[self convertPoint:point toView:subview] withEvent:event] && ![subview isKindOfClass:[UITableView class]]) {
            return NO;
        }
    }
    return YES;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接