简体   繁体   中英

Limiting the scrollable area in UIScrollView

I have a UIScrollView that is scrolling a fairly large UIView.

At certain times I want to limit the area the user can scroll around in. For example, I may only want to allow them to view the bottom quarter of the view.

I am able to limit the area by overriding scrollViewDidScroll and then calling setContentOffset if the view has scrolled too far. But this way I can't get it bounce back as smoothly as the UIScrollView can naturally do when scrolling beyond the bounds of the UIView.

Is there a better way to limit the scrollable area in a UIScrollView?

I would change the contentSize property of the scroll view to the size of the area you want the user to be able to scroll around in and adjust the frame.origin of the subview such the upper left boundary you want appears at (0, 0) relative to the scroll view. For example, if your view is 800 points tall and you want to show the bottom quarter, set the height of contentSize to 200 and set the y component of view.frame.origin to -600.

I've found something that works for me. It let's you scroll to point 0,0 but no further:

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

You could do the same for top, bottom or right (x or y)

Another approach is to override the UIScrollView 's method:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event .

Returning YES will allow the user to scroll. Returning NO will not.

NOTE: This will disable all touches to any views imbedded inside the UIScrollView that pointInside returns NO to. Useful if the area you don't want to scroll from doesn't have any interaction.


This example only allows the UIScrollView to scroll when the user is scrolling over a UITableView . (A UITableView and two UIViews are imbedded inside the 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;
}

a small improvement on Yoko's answer in Swift 4 will be

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()
    }
}

which will make the scrollview animate really similar to what its supposed to do. The slower drag when you are in the "bounce" area will not work and animation duration has to depend on the distance (not constant like here) if you want to be exact. You can also try to do this logic in scrollViewDidScroll and see how it differs. The key thing is that setContentOffset(_:,animated:) has to be with animated: false so that the UIViewPropertyAnimator 's block can capture it and animate it

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM