iOS基础UI瀑布流界面简单搭建

ios UI基础瀑布流 顾名思义是将界面以瀑布流水般的展现出来,使用瀑布流,首先对数据进行懒加载,传入数据后,使用UIcollectionView控件在main.storyboard里进行简单的布局

![collectionViewitem布局]


collectionView已经设置的和屏幕等宽等高了

然后使用collectionViewDataSource代理  flowLayOut连一根线到View controller,然后遵循协议,实现代理方法

-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView            

 //返回组


-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section        

//返回item


-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath           

//创建UICollectionViewCell

注意在这里我们使用的是原型cell,所以不会出现cell不存在的情况,所以不用担心cell重用的问题,因为系统一加载数据就会创建一个cell,接下来我们要获取cell中的imageViewLabel,使用[cell viewWithTag:]方法,这个方法返回值是一个UIview,我们要用一个UIView来接收,所有的控件都继承自UIView,因此imageUIImageView来接收,LabelUI label来接收,(是否碰到报错的情况呢?

```_***dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:],*** /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3505.16/UICollectionView.m:3600

*2016-03-11 11:01:47.920 瀑布流[943:32280] Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'could not dequeue a view of kind: UICollectionElementKindCell with identifier haha - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

 First throw call stack```

这个错误很简单,想想自己是否将main.storyboard里的UICollectionViewCell中的identifier和你.m中定义的标识是否一致)

接下来运行看一下数据有没有加载到,背景是黑色的很奇怪吗?UICollectionView控件默认的背景就是黑色,不用担心

****

界面搭建完了发现布局不是我们想要的效果,所以,我们将要自定义一个layout,让它继承自UICollectionViewFlowLayOut(为什么不继承自UICollectionViewLayOut呢?是因为UICollectionViewFlowLayOut里的属性UICollectionViewLayOut里面没有)既然是自定义的layout,那么一上来就有6个方法,这是系统的方法不必纠结


-(void)prepareLayout

//layout运行加载前调用的方法


-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath

//根据indexPath获取当前控件的属性


-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect

//返回可视区域的控件属性,程序一运行我们就能看到的


-(BOOL)shouldInvalidateLayoutForBoundsChange(CGRect)newBounds

//当界面改变的时候,需要重新更新界面的bounds



-(CGSize)collectionViewContentSize

//


-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity

//当界面滚动的时候调用的方法,需要做动画处理的方法


写完这6个方法后,

****

接下来  **框架分析**

第一步:就开始分析首先我们考虑 程序一运行我们所看到的界面是改变后的界面,那么在这6个方法里有一个方法是返回可视区域内的控件属性-(NSArray<UICollectionViewLayoutAttributes *>方法,在这里返回的是改变后的item的集合,也就是说在程序运行之前一定调用了一个方法改变之前的item


第二步:第一步中程序运行,在cmdR的时候模拟器就给我们展现出了这样瀑布流的界面,在layout加载前就调用这个方法就是-(void)prepareLayout方法,在这里首先我们应该遍历每一个原先的item,然后调用根据indexPath获得当前item属性的方法得到改变后的item添加到一个可变数组中,(在这里,遍历原先的item 需要获取原先的collectionView里的item的数量,indexpath索引值通过NSindexPath方法进行获取


第三步:既然要改变item的属性那么看看这6个方法,我们发现有一个根据indexPath获得当前item的属性方法-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath,我们肯定是要在这个方法里做文章,首先想要改变item的属性,那就要先获取原来的item,然后通过计获取的新的item的坐标和尺寸,将原来的itemframe改变。



****

算法分析

第一步:先计算控件的尺寸:

item的宽 (屏幕的宽度也就是CollectionView的宽度-item之间的最小的间隙*(每一行的item数量-1)-左间距-右间距)/每一行的item的数量

在这里每一行的item的数量我们是不知道的,这个希望别人自己去定义,所以我们希望别人自己定义,那就不能把这个属性定义在.m文件里,所以我们在.h文件中定义这个属性,不排除有得人乱定义,将每一行的item设置为0的情况,所以在.m文件里需要重写一下这个属性的get方法。(如果为0则返回3


item的高 高/宽*宽

(宽在第一步里是已知的,高/宽的比率需要写一个方法来实现)我们在layout里是无法直接知道控件的宽和高的,所以自己的事自己不能直接去做,那么我们就设置一个代理,代理三部曲就不多说了,在这里判断一下什么时候用@optional@required,还有dataSourceDelegate  ,首先看这个方法会不会影响程序运行,有影响则用dataSource,否则用Delegate,必须实现的话就用required否则用optional,套路就不说了,这里说几个注意点,将这个代理在ViewController中用的时候在获取item宽高数据的时候我们发现需要一个indexPath的索引值,我们看回去在FlowLayOut中计算item尺寸的方法里我们有indexPath,所以需要传一个indexPath的索引值,随之代理中也要传一个IndexPath的索引值,在返回item宽高比率的时候别忘记宽高是NSinteger类型,而代理中返回的是CGFloat类型的,所以要进行强转。


根据九宫格算法

itemX 左边距+当前item的列数*(item的宽+item之间的间隙)

这里唯一不知道的变量是当前item的列数,我们需要去定义一个当前列(*为什么不用indexPath.item(当前控件cell是处于第几组的第几个item)%这一行的控件的个数,虽然也可以获得当前列,但是它使控件的排列方式是第一行排列完紧接着排列第二行,很死板,这样会导致有可能最后的瀑布流看起来很难看*),然后去获取最小的Y值的当前列,然后排列的时候一行排完后下一行按照上一行最小的Y进行排列,变得很灵活,不管出现什么情况最后的图案也是比较好看的,说到这里应该发现我们应该先去计算Y的值,这样才能获取到最小Y的值

定义一个数组去挨个存放每一个控件的Y值,做更新CGRectMaxY得到每一个控件Y的最大值存放在数组中,然后遍历这个存放Y的最大值的数组,定义一个Y值,重写它的get方法,获取到存放最大Y值数组的当前的Y值,然后用定义的Y与获取到的当前的Y值做比较将最小的那个赋值给定义的Y,就得到我们的最小Y ,同时也能得到我们的当前列


itemY 最小的Y+列间距


在这里别忘记要更新存放在数组中的最大的Y值!!!

最后设置滚动范围


容易遇到的不报错Bug1.定义的数组没有进行初始化操作 2.遍历的时候该遍历谁要明确3.更新Y值的时候,注意将CGFloat转换成NSString类型 4.实现代理方法中记得将NSInteger强转。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值