[iOS]UIScrollview自定义分页的实现方法

来源:互联网 发布:apache c cgi 编辑:程序博客网 时间:2024/04/28 11:19

UIScrollView及其子类UITableView, UICollectionView为iOS开发带来了极大的方便, 其分页(pagingEnabled)功能也很常用, 但是功能却有些局限, 页只能按UIScrollviewbounds尺寸划分, 如果要实现自定义分页宽度或高度就需要一些技巧.

方法一

类似iOS 6中MobileSafari, 如图所示的分页方法:

HGPageScrollView_Screen_shot_2

 

StackOverflow上提供的实现思路如下:

 

  1. UIScrollviewbounds限制再屏幕中间那一页的位置
  2. 禁用UIScrollviewclipsToBouds, 从而能显示超出bounds的内容

此时已经可以在借助UIScrollview原有分页功能的基础上实现了"视觉上"与MobileSafari类似的效果, 但因为UIScrollview的尺寸限制, 超出范围的触摸事件不会被UIScrollview收到, 影响体验, 要把触摸事件传递给UIScrollview, 方法如下:

  1. 用一个全屏的(或者其他你想要的尺寸)UIView子类, 这里我们命名为CustomUIView来做UIScrollviewsuperView
  2. 重写CustomUIViewhitTest:withEvent方法:

     - (UIView *) hitTest:(CGPoint) point withEvent:(UIEvent *)event {   if ([self pointInside:point withEvent:event]) {     return scrollView;   }   return nil; }

    通过重写hitTest, 就可以将CustomUIView接受到的触摸事件传递给UIScrollView

方法二

前一种方法保留了UIScrollView原有的分页功能, 而下面这种更加灵活的自定义分页方法则完全自制了一个分页功能:

DMPagingScrollView就是一个很好的实现, 其自定义分页的思路大致如下(需要参考DMPagingScrollView.m代码):

  1. 用实例变量pageWidth记录自定义分页的宽度
  2. 通过实现UIScrollView的delegate方法, 来记录每次拖动UIScrollView动作的相关参数(位移, 加速度…)
  3. scrollViewDidEndDecelerating:等一些标志拖动结束的delegate方法中调用snapToPage方法, 在snapToPage方法中, 借助之前收集的动作参数计算出contentOffset, 并通过setContentOffset:offset animated:YES来移动.

这种方式的关键就在contentOffset的计算中(DMPagingScrollView.m pageOffsetForComponent:方法), 如果计算的不正确, 就会严重影响拖动UIScrollView的手感.

DMPagingScrollView通过subclassing UIScrollview将自定义分页相关的过程操作封装在DMPagingScrollView里面, 同时支持横向纵向的自定义分页, 十分方便. 类似地, 我们也可以对UITableView,UICollectionView进行同样的处理.

此外, 通过参考pageOffsetForComponent:中的计算方法, 我们甚至可以让每一页的宽度都不同, 从而大大提升了分页的灵活性, 比如我实现的这种:

- (CGFloat)pageOffset {    CGFloat totalWidth = self.collectionView.contentSize.width;    CGFloat visibleWidth = self.collectionView.bounds.size.width;    CGFloat currentOffset = self.collectionView.contentOffset.x;    CGFloat dragVelocity = _dragVelocity.x;    CGFloat dragDisplacement = _dragDisplacement.x;    NSInteger nearestIndex = 0;    CGFloat minDis = CGFLOAT_MAX;    CGFloat nearestOffset = 0;    CGFloat prevOffset = 0;    CGFloat nextOffset = 0;    CGFloat totalOffset = 0;    // self.pagesWidths is an NSArray that contains each page's width    for (NSInteger i = 0; i < self.pagesWidths.count; i++) {        CGFloat dis = ABS(totalOffset - currentOffset);        if (dis < minDis) {            minDis = dis;            prevOffset = nearestOffset;                        nearestOffset = totalOffset;            if (i == self.pagesWidths.count - 1) {                nextOffset = totalOffset;            }            else {                nextOffset = totalOffset + [(NSNumber *)self.pagesWidths[i] floatValue];            }        }        totalOffset += [(NSNumber *)self.pagesWidths[i] floatValue];    }    NSInteger lowerIndex;    NSInteger upperIndex;    CGFloat lowerOffset;    CGFloat upperOffset;    if (currentOffset - nearestOffset < 0) {        lowerIndex = nearestIndex - 1;        upperIndex = nearestIndex;        lowerOffset = prevOffset;        upperOffset = nearestOffset;    }    else {        lowerIndex = nearestIndex;        upperIndex = nearestIndex + 1;        lowerOffset = nearestOffset;        upperOffset = nextOffset;    }    CGFloat newOffset;    if (ABS(dragDisplacement) < DRAG_DISPLACEMENT_THRESHOLD || dragDisplacement * dragVelocity < 0) {        if (currentOffset - lowerOffset > upperOffset - currentOffset) {            newOffset = upperOffset;        } else {            newOffset = lowerOffset;        }    } else {        if (dragVelocity > 0) {            newOffset = upperOffset;        } else {            newOffset = lowerOffset;        }    }    if (newOffset > totalWidth - visibleWidth)        newOffset = totalWidth - visibleWidth;    if (newOffset < 0)        newOffset = 0;    return newOffset;}
0 0
原创粉丝点击