CollectionView自定义pagingEnabled属性的翻页区域

Demo地址:https://github.com/Rogue24/JPCustomScrollPageSize

虽说 scrollView 有 pagingEnabled 这个属性可以实现翻页的效果,如果是整个翻页的区域为 scrollView 的 size 那就好,但想要只翻半个 scrollView 的宽度那怎么办呢?

实际上,scrollView 的 pagingEnabled 的区域是根据 scrollView 的 size 来决定,也就是scrollView有多宽就翻多少啦。

如果是单纯的 scrollView 那就好办,直接设置 scrollView 的区域为想要翻页的尺寸,如果比屏幕小就设置 scrollView 的 clipsToBounds 为NO就好了,然后修改 hittest 让多余的空位的点击事件回传给 scrollView 就好了!

可是如果是 collectionView 并且设置其为屏幕一半的大小的话,由于 collectionView 的重用机制的原因,collectionView 区域(不是 contentSize,是 bounds)以外的 cell 并不会提前显示,然后一直滚的话,超出区域的 cell 也会被立即重用直接移位到后面了。

所以。。。

一个简单粗暴的方法:使用一个占位的 scrollView 来实现!

这里我的翻页宽度为cell的宽度.gif
  1. 创建 collectionView 并实现的基本数据源、代理的方法,这里我的 collectionView 的数据源和代理都为 collectionView 自身(self),方便管理。
self.delegate = self;
self.dataSource = self;
self.scrollEnabled = NO; // 不需要自身来进行滚动
  1. 在 collectionView 上添加一个占位的 scrollView(专门用来翻页用的)。
// 先从collectionViewLayout中获取翻一页的宽度
// 我这里的一页宽度就是一个cell的宽度加上间距
JPFlowLayout *flowLayout = (JPFlowLayout *)layout;
CGFloat width = flowLayout.itemSize.width + flowLayout.minimumLineSpacing;

// 创建占位的scrollView,设置成一页的宽度
// 这里只有水平滚动,所以只设置宽度即可,位置则随意
UIScrollView *placeholderSV = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, width, 0)]; 
placeholderSV.pagingEnabled = YES;
placeholderSV.delegate = self;
placeholderSV.showsHorizontalScrollIndicator = NO;

// 添加到collectionView上
[self addSubview:placeholderSV];
self.placeholderSV = placeholderSV;
  1. 修改 hitTest 方法,让占位 scrollView 以外的点击事件都能接收到。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    return self.placeholderSV;
}
  1. 实现 scrollViewDidScroll: 方法。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView == self.placeholderSV) {
        // 让占位scrollView来控制collectionView的偏移量
        self.contentOffset = scrollView.contentOffset;
    }
}
  1. 设置占位 scrollView 的 contentSize(总页数的宽度)。
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    // 我选择在collectionView的数据源方法中设置contentSize,保证跟collectionView的contentSize保持一致
    self.placeholderSV.contentSize = CGSizeMake(self.placeholderSV.jp_width * MaxItemCount, 0); // 翻页宽度 * 数据数量 
    return MaxItemCount;
}

现在能实现基本的翻页效果了,但是,此时所有的点击事件都被占位 scrollView 拦截了,怎么让 cell 的点击传给 collectionView 呢?

在占位 scrollView 添加手势传递事件

  • 例如添加一个 tap 手势
// 添加一个tap手势
[placeholderSV addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)]];

// 监听tap手势
- (void)tap:(UITapGestureRecognizer *)tapGR {
    CGPoint point = [tapGR locationInView:self];
    NSIndexPath *indexPath = [self indexPathForItemAtPoint:point];
    if (indexPath) {
        [self collectionView:self didSelectItemAtIndexPath:indexPath];
    }
}

#pragma mark - UICollectionViewDataSource
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    // 使用占位scrollView滚动目标位置(这里我是滚到屏幕中心)
    [self.placeholderSV setContentOffset:CGPointMake(indexPath.item * self.placeholderSV.jp_width, 0) animated:YES];
}

防止拦截 collectionViewCell 上的需要响应的子控件(例如 button)

只要在 hitTest 方法上判断触碰的 view 是否为需要响应的子控件,是则返回该控件,否则还是返回占位 scrollView,判断条件可以使用 tag 值,也可以判断 view 的类型等等。

  • 在 cell 上添加一个 button
// 这里我使用设定好的可以响应的tag值
UIButton *button = ({
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
    // 设置可以响应的tag值
    btn.tag = JPInteractionEnabledTag;
    btn.backgroundColor = [UIColor greenColor];
    btn.titleLabel.font = [UIFont systemFontOfSize:30];
    [btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(btnDidClick) forControlEvents:UIControlEventTouchUpInside];
    btn;
});
  • 在 hitTest 方法里面进行判断
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *view = [super hitTest:point withEvent:event];
    // 如果该view的tag为可以响应的tag值就返回该控件
    if (view.tag == JPInteractionEnabledTag) {
        return view;
    }
    return self.placeholderSV;
}

结语

此时基本效果能实现了,然而弊端还是有的,因为占位 scrollView 拦截了 collectionView 的所有点击事件,而我这里只处理了单击事件,例如 collectionView 的 cell 拖动,那么就要把占位 scrollView 的拦截关掉。
如果有更好的实现方案请告诉我!

最后献上Demo一份:https://github.com/Rogue24/JPCustomScrollPageSize

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容