UIScrollView custom paging size

2019-01-16 05:51发布

paging in UIScrollView is a great feature, what I need here is to set the paging to a smaller distance, for example I want my UIScrollView to page less size that the UIScrollView frame width. Thanks

10条回答
Melony?
2楼-- · 2019-01-16 06:11

There is a UIScrollView delegate method you can use. Set your class as the scroll view's delegate, and then implement the following:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    CGFloat kMaxIndex = 23;
    CGFloat targetX = scrollView.contentOffset.x + velocity.x * 60.0;
    CGFloat targetIndex = round(targetX / (kCellWidth + kCellSpacing));
    if (targetIndex < 0)
        targetIndex = 0;
    if (targetIndex > kMaxIndex)
        targetIndex = kMaxIndex;
    targetContentOffset->x = targetIndex * (kCellWidth + kCellSpacing);
}

The velocity parameter is necessary to make sure the scrolling feels natural and doesn't end abruptly when a touch ends with your finger still moving. The cell width and cell spacing are the page width and spacing between pages in your view. In this case, I'm using a UICollectionView.

查看更多
成全新的幸福
3楼-- · 2019-01-16 06:15

Swift 4.1 solution that simplifies reusing:

protocol ScrollViewCustomPageSize: UIScrollViewDelegate {
    var pageSize: CGFloat { get }
    func getTargetContentOffset(scrollView: UIScrollView, velocity: CGPoint) -> CGFloat
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)
}

extension ScrollViewCustomPageSize {
    func getTargetContentOffset(scrollView: UIScrollView, velocity: CGPoint) -> CGFloat {
        let targetX: CGFloat = scrollView.contentOffset.x + velocity.x * 60.0
        var targetIndex: CGFloat = round(targetX / pageSize)
        if targetIndex < 0 {
            targetIndex = 0
        }

        if velocity.x > 0 {
            targetIndex = ceil(targetX / pageSize)
        } else {
            targetIndex = floor(targetX / pageSize)
        }

        var offsetX = targetIndex * pageSize - scrollView.contentInset.left
        offsetX = min(offsetX, scrollView.contentSize.width - scrollView.bounds.width + scrollView.contentInset.right)

        return offsetX
    }
}

Just conform to ScrollViewCustomPageSize protocol in your UIScrollView/UITableView/UICollectionView delegate and you are done, e.g.:

extension MyCollectionViewController: ScrollViewCustomPageSize {
    var pageSize: CGFloat {
        return 200
    }

    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        targetContentOffset.pointee.x = getTargetContentOffset(scrollView: scrollView, velocity: velocity)
    }
}

For a fancy scrolling I also recommend to set collectionView.decelerationRate = UIScrollViewDecelerationRateFast

查看更多
Evening l夕情丶
4楼-- · 2019-01-16 06:16

I had the same problem short ago. My aproach was to add a second UIScrollView to the scrollview. So you can switch to the page. On that page it seems than if the page is bigger than the screen. I hope it works also in your situation. ;-)

Sandro Meier

查看更多
Bombasti
5楼-- · 2019-01-16 06:18
  1. Change your scrollView size to the page size you want
  2. Set your scroll.clipsToBounds = NO
  3. Create a UIView subclass (e.g HackClipView) and override the hitTest:withEvent: method

    -(UIView *) hitTest:(CGPoint) point withEvent:(UIEvent *)event
    {     
        UIView* child = [super hitTest:point withEvent:event]; 
        if (child == self && self.subviews.count > 0)  
        {
            return self.subviews[0];
        }
        return child;
    }
    
  4. Set the HackClipView.clipsToBounds = YES

  5. Put your scrollView in this HackClipView (with the total scrolling size you want)

See this answer for more details

Update: As stated in lucius answer you can now implement the UIScollViewDelegate protocol and use the - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset method. As the targetContentOffset is a pointer. Using this method will not guarantee you the same result with scroll view pages as the user can scroll through many pages at once. But setting the descelerationRate to fast will almost give you the same result

查看更多
登录 后发表回答