UICollectionView应用(一)--iOS类头条首页切换效果

之前在看iCarousel源码的时候发现其中的效果都是可以用UICollectionView来实现的,作为UITableView的替代框架,UICollectionView更为先进,正好最近有一个分类条目在上面,可以滑动和点击分类切换页面的需求。在iPhone上面关于左右滑动的切换页面的控件已经很多了,像类似于今日头条、网易新闻等咨询类app的首页都是使用这种架构,京东app的好东西也是这种架构,不过是多了一个选中的放大效果。没有时间看开源大神的想法,这里就自己用UICollectionView实现了一个。(Demo没有做例子,如果想要直接使用的话请留言,我会加上)


展示图片

LIXScrollTopBarView

首先主要提供了基础的两种切换页面的效果,提供了多种顶部titleBar的多种选中效果。包括颜色提供渐变和突变两种样式,大小提供了根据颜色条靠近的距离时间大小的渐变,可以定义底部选中条的颜色,可以定义每个分类下选中条的大小。最近看到优酷、微博等app在选中条上面做文章,感觉也是挺有心意,以后会考虑加上。底部的内容视图的话可以编辑任意一个cell,总之下面的内容视图就是一个UICollectionView,可以实现各种自定义的layout。

使用方法

首先是titleBar的几种样式可供选择,在使用的时候可以直接设置scrollTitleType属性就可以了,下面提供几种style的默认样式。当然你也可以传入自己的flowLayout。

  • LIXScrollTopBarType_default


    展示图片
  • LIXScrollTopBarType_transform & LIXScrollTopBarType_default


    defaultcolor&transform
  • LIXScrollTopBarType_transform & LIXScrollTopBarType_gradient


    colorGradient

各种样式可以叠加使用,也可以单独使用,叠加的时候可以出现各种效果,就不一一展示了。

关于内容视图的滚动也提供了一种加速度的样式,最近看到百度外卖的app有加入这种模式。但是在实践的过程中发现,这种方式在滑动过程中进行屏幕旋转的过程中会出现crash。如不不支持横竖屏适配的话是完全没问题的。

  • contentCellScrollStyle = LIXScrollTopBarContentScrollStyle_dynamic


    加速度

加速度效果使用的是dynamic框架,是苹果提供用来模仿物理效果的。其原理也是计算每个cell距离触点的位置,然后改变每个cell的frame,在frame改变的过程中加入物理加速度的效果。

结构

结构图

如图所示,使用的类比较多,因为是在UICollectionView的基础上进行的封装,所以遵守的是UICollectionView的使用方法。

  • LIXScrollTopBarView是主要的类,主要是管理上下两个UICollectionView的同步问题,主要是要根据下面UICollectionView的移动距离,来同步上面UICollectionView的选中态。
  • LIXScrollTopBarTitleBarView可能根据名字很难看出来是干什么的,这个是下面的指示条,如果你困惑为什么一个指示条还要单独建一个类的话,其实就是万恶的设计想要每个title分类下都展示和文字同样长度的指示条(为什么说是万恶的设计那,因为他最后又给去掉了,让展示同样长度的指示条)
  • LIXScrollTopBarTitleCell是分类的cell,LIXScrollTopBarCell是下面UICollectionView的Cell,里面的又是一个UICollectionView,最里面的cell是LIXScrollTopBarContentCell。
  • flowLayout,因为是简单的流式布局,所以直接使用的是flowLayout,对于flowLayout的话,就是每个UICollectionView就对应一个flowLayout
  • model分类下的是每个页面的model(LIXScrollTopBarViewDataSourceModel)和整个页面的数据(LIXScrollTopBarViewDataSource),整个页面的数据,也可以说成是manager吧。
  • LIXScrollTopBarTitleCellLayoutAttributes这个是定制的UICollectionViewCellAttributes,因为有部分属性需要改变,但是attributes类并没有提供,这里继承扩展一些。

后期优化使用工厂方法来创建对象,还有就是类的命名问题,真的成了开发效率的瓶颈了,这个地方需要加强一下。

核心思想

核心思想是最大的可定制化吧,刚开始的时候想的是可以使用viewController之间的自定义转场也可以实现这种效果,而且可以完美的管理每个view的生命周期;或者是直接使用ScrollView,计算每个page页面所在的位置,像iCarousel那样建立自己的重用队列,管理重用问题;当然也可以像iCarousel那样,不使用scrollView,直接计算每个page页面的transform,自己管理重用。后来考虑到UICollectionView有现成的API可以调用,而且对于flowLayout分离的方式可以最大程度的提供灵活的切换方式(完全可以新建一个flowLayout,创建不拘于实现过的几种方式)。

UICollectionView基础

并没有使用很复杂的UICollectionView的知识吧,主要的就是flowLayout和每个cell的交互花了点心思,这里还是需要一些UICollectionView的基础知识的。下面列出来几点用到的。

自定义UICollectionViewCellAttributes

attributes类提供的功能主要是定制cell的frame,transFrom等属性,一个attributes对应一个cell,管理一个cell的layout。但是有些没有进行默认的提供,比如我们想改变字体的颜色,我们就可以定制其子类,加上一个fontColor属性,当滑动到相应的cell的时候就可以发生刷新cell的样式。这是每个title cell可以定制化的基础。

UICollectionViewFlowLayout与Attributes的交互

既然每个cell都对应一个attributes属性的话,那么layout做的事情就是管理这些attributes的变化。UICollectionViewFlowLayout是UICollectionViewLayout的定制子类,是一种特定的流式布局,用起来比直接使用layout简单一下,定制起来也有些不同。这里只说到用到的flowLayout。在我的理解看来,layout类的功能就是刷新每个attributes。


+ (Class)layoutAttributesClass {
    
    return [JDScrollTopBarTitleCellLayoutAttributes class];
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    
    return YES;
}

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    
    NSArray *layoutAttributesArray = [super layoutAttributesForElementsInRect:rect];
    
    for (UICollectionViewLayoutAttributes *attributes in layoutAttributesArray) {
        
        //如果判断交集的话会出现复用的时候属性不对的问题
//        if(CGRectIntersectsRect(attributes.frame, rect)) {
            [self applyAttributes:attributes forVisibleRect:rect];
//        }
    }
    
    return layoutAttributesArray;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    UICollectionViewLayoutAttributes *layoutAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
    
    CGRect visibleRect = CGRectMake(self.collectionView.contentOffset.x, self.collectionView.contentOffset.y, CGRectGetWidth(self.collectionView.bounds), CGRectGetHeight(self.collectionView.bounds));
    
    [self applyAttributes:layoutAttributes forVisibleRect:visibleRect];
    
    return layoutAttributes;
}

一个是返回attributes数组,一个是返回每个indexPath对应下的attributes。layoutAttributesClass方法是将默认的attributes类替换成我们的attributes类,
shouldInvalidateLayoutForBoundsChange是bounds发生改变的时候更新attributes,这里boundsChange也可以理解成UIScrollVIew滚动,因为UIScrollView的实现方式就是改变view的bounds,所以在滚动的时候就会更新attributes了。

UICollectionViewCell接收Attributes属性

- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
    [super applyLayoutAttributes:layoutAttributes];
    
    self.titleLabel.textColor = [(JDScrollTopBarTitleCellLayoutAttributes *)layoutAttributes textColor];
    
    //使用shouldRasterize会导致label文字模糊
    //    self.layer.shouldRasterize = [layoutAttributes valueForKey:@"shouldRasterize"];
    
    CGFloat fontSize = 16 * [(JDScrollTopBarTitleCellLayoutAttributes *)layoutAttributes fontScale];
    self.titleLabel.font = [UIFont systemFontOfSize:fontSize];
    self.layer.affineTransform = layoutAttributes.transform;
}

在这个方法里面,attributes发生更新的时候,cell就能过得到通知,应该是UICollectionView在底层做了一个观察者吧。layer有一个shouldRasterize属性,该方法可以栅格化layer,并且触发离屏渲染,缓存栅格化之后的数据,这样在cell比较多的情况下会有很好的内存表现。但是这里会引发一个问题,就是label问题模糊,不知道是否是渲染文字的时候出现的问题,有大神知道的话望留言告知。

具体实现

基于UICollectionView的以上各种,完成了ScrollTopBar。通过TopBarCell和titleCell之间的比例,可以计算出滑动之后标题指示条的位置。根据指示条中心的位置和titleCell的位置之间的差值来计算选中的titleCell。具体实现可以参照源码(github地址:https://github.com/lixuzong/LIXScrollTopBarDemo)。具体讲一下开发中遇到的问题。

问题及解决

  • 最棘手的问题是,在滑动底部的UICollectionView的时候,没办法让titleBar的cell的attributes发生更新操作。这里的解决方法是在didScroll方法里面计算出相应的cell,并且主动调用cell的applyAttributes方法。
  • 需要定制选中指示条,但是指示条是计算选中cell的基础,不能随意变动,所以设计成一个view树,最外层的view是透明的,里面的子view提供颜色,这样就可以根据需求给指示条做动画,也可以根据每个不同的cell做不同的变化等。
  • 关于数据的同步,感觉处理的不够好,但是为了偷懒,是用一个单例来实现了,在网上也找到替换单例的方法,就是注入依赖替换单例,说白了就是在创建的时候将数据传入,跟JS里面传递prop有点类似吧。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容