iOS瀑布流实现(Swift)
这段时间突然想到一个很久之前用到的知识-瀑布流,本来想用一个简单的方法,发现自己走入了歧途,最终只能狠下心来重写UICollectionViewFlowLayout.下面我将用两种方法实现瀑布流,以及会介绍第一种实现的bug.
<1>第一种
1)首先调用随机函数,产生随机高度,并把它保存到数组中
复制代码
-(CGSize)collectionView:(UICollectionView)collectionViewlayout:(UICollectionViewLayout)collectionViewLayoutsizeForItemAtIndexPath:(NSIndexPath)indexPath{
CGFloatcellW=100;
CGFloatcellH=100+(arc4random()%80);
[self.heightArrayMaddObject:@(cellH)];
returnCGSizeMake(cellW,cellH);
}
复制代码
2)在设置cell的frame的地方,通过取余,取整确定cell的高度,并设定cell的frame
复制代码
-(UICollectionViewCell)collectionView:(UICollectionView)collectionViewcellForItemAtIndexPath:(NSIndexPath)indexPath{
UICollectionViewCellcell=[self.collectionViewdequeueReusableCellWithReuseIdentifier:IDforIndexPath:indexPath];
//当前处于多少行
NSIntegernum1=indexPath.row/count;
//当前处于多少列
intnum2=indexPath.row%count;
CGFloatcellX=num2100+(num2+1)margin;
CGFloatcellY=0;
for(inti=0;i NSIntegerposition=num2+i3;
cellY+=[self.heightArrayM[position]floatValue]+margin;
}
CGFloatcellW=100;
CGFloatcellH=cellHeight;
cell.frame=CGRectMake(cellX,cellY,cellW,cellH);
//cell.backgroundColor=[UIColorredColor];
cell.backgroundColor=[UIColorcolorWithRed:(arc4random()%250)/250.0green:(arc4random()%250)/250.0blue:(arc4random()%250)/250.0alpha:1.0];
//NSLog(@"%@",NSStringFromCGRect(cell.frame));
returncell;
}
复制代码
弊端:其实这种方法的弊端,相信从上面的动态图中可以看出来,当往上面滑的时候,由于cell的循环机制,下面的cell的会消失,但是由于高度不一致,同时撤销的是最后一行的cell,所以下面的cell在屏幕上就会消失.
下面附上第一种方法的源代码:
复制代码
#import"ViewController.h"
#definemargin10
#definecount3
#definecellHeight[self.heightArrayM[indexPath.row]floatValue]
staticNSStringconstID=@"cell";
@interfaceViewController()
@property(weak,nonatomic)IBOutletUICollectionViewcollectionView;
@property(nonatomic,strong)NSMutableArrayheightArrayM;
@end
@implementationViewController
-(NSMutableArray)heightArrayM{
if(_heightArrayM==nil){
_heightArrayM=[NSMutableArrayarray];
}
return_heightArrayM;
}
-(void)viewDidLoad{
[superviewDidLoad];
[self.collectionViewregisterClass:[UICollectionViewCellclass]forCellWithReuseIdentifier:ID];
self.collectionView.dataSource=self;
self.collectionView.delegate=self;
//设置collectionView
[selfsetupCollectionView];
}
//设置collectionView的布局
-(UICollectionViewFlowLayout)setupCollectionLayout{
UICollectionViewFlowLayoutflowLayout=[[UICollectionViewFlowLayoutalloc]init];
flowLayout.minimumInteritemSpacing=margin;
flowLayout.minimwww.baiyuewang.netumLineSpacing=margin;
flowLayout.sectionInset=UIEdgeInsetsMake(margin,margin,margin,margin);
returnflowLayout;
}
//设置collectionView
-(void)setupCollectionView{
self.collectionView.collectionViewLayout=[selfsetupCollectionLayout];
}
#pragmamark-UICollectionViewDataSouce
-(NSInteger)collectionView:(UICollectionView)collectionViewnumberOfItemsInSection:(NSInteger)section{
return60;
}
-(UICollectionViewCell)collectionView:(UICollectionView)collectionViewcellForItemAtIndexPath:(NSIndexPath)indexPath{
UICollectionViewCellcell=[self.collectionViewdequeueReusableCellWithReuseIdentifier:IDforIndexPath:indexPath];
//当前处于多少行
NSIntegernum1=indexPath.row/count;
//当前处于多少列
intnum2=indexPath.row%count;
CGFloatcellX=num2100+(num2+1)margin;
CGFloatcellY=0;
for(inti=0;i NSIntegerposition=num2+i3;
cellY+=[self.heightArrayM[position]floatValue]+margin;
}
CGFloatcellW=100;
CGFloatcellH=cellHeight;
cell.frame=CGRectMake(cellX,cellY,cellW,cellH);
//cell.backgroundColor=[UIColorredColor];
cell.backgroundColor=[UIColorcolorWithRed:(arc4random()%250)/250.0green:(arc4random()%250)/250.0blue:(arc4random()%250)/250.0alpha:1.0];
//NSLog(@"%@",NSStringFromCGRect(cell.frame));
returncell;
}
-(CGSize)collectionView:(UICollectionView)collectionViewlayout:(UICollectionViewLayout)collectionViewLayoutsizeForItemAtIndexPath:(NSIndexPath)indexPath{
CGFloatcellW=100;
CGFloatcellH=100+(arc4random()%80);
[self.heightArrayMaddObject:@(cellH)];
returnCGSizeMake(cellW,cellH);
}
@end
复制代码
<2>下面介绍第二种(Swift实现)
效果图如下所示:
这种实现方法就是比较成熟的了,我把它封装成一个类.其实主要是实现三个函数
1)重写父类的prepare方法,准备所有cell的样式
复制代码
extensionWaterfallLayout{
//prepare准备所有Cell的布局样式
overridefuncprepare(){
super.prepare()
//0.获取item的个数
letitemCount=collectionView!.numberOfItems(inSection:0)
//1.获取列数
letcols=dataSource?.numberOfColsInWaterfallLayout?(self)??2
//2.计算Item的宽度
letitemW=(collectionView!.bounds.width-self.sectionInset.left-self.sectionInset.right-self.minimumInteritemSpacingCGFloat((cols-1)))/CGFloat(cols)
//3.计算所有的item的属性
foriinstartIndex.. //1.设置每一个Item位置相关的属性
letindexPath=IndexPath(item:i,section:0)
//2.根据位置创建Attributes属性
letattrs=UICollectionViewLayoutAttributes(forCellWith:indexPath)
//3.随机一个高度
guardletheight=dataSource?.waterfallLayout(self,indexPath:indexPath)else{
fatalError("请设置数据源,并且实现对应的数据源方法")
}
//4.取出最小列的位置
varminH=colHeights.min()!
letindex=colHeights.index(of:minH)!
minH=minH+height+minimumLineSpacing
colHeights[index]=minH
//5.设置item的属性
attrs.frame=CGRect(x:self.sectionInset.left+(self.minimumInteritemSpacing+itemW)CGFloat(index),y:minH-height-self.minimumLineSpacing,width:itemW,height:height)
attrsArray.append(attrs)
}
//4.记录最大值
maxH=colHeights.max()!
//5.给startIndex重新复制
startIndex=itemCount
}
}
复制代码
2)返回设置cell样式的数组
overridefunclayoutAttributesForElements(inrect:CGRect)->[UICollectionViewLayoutAttributes]?{
returnattrsArray
}
3)返回当前的contentSize
overridevarcollectionViewContentSize:CGSize{
returnCGSize(width:0,height:maxH+sectionInset.bottom-minimumLineSpacing)
}
总结:
在下面我封装的这个类中,只需要遵守我的数据代理源协议并且实现我的协议中的两个方法,传给我对应得高度(我这里是传的随机的),可选的方法,若是不实现,会有一个默认值,就可以实现该功能.协议如下:
@objcprotocolWaterfallLayoutDataSource:class{
funcwaterfallLayout(_layout:WaterfallLayout,indexPath:IndexPath)->CGFloat
@objcoptionalfuncnumberOfColsInWaterfallLayout(_layout:WaterfallLayout)->Int
}
完成代码如下所示:
ViewController.swift中的代码:
复制代码
importUIKit
extensionUIColor{
classfuncrandomColor()->UIColor{
returnUIColor(colorLiteralRed:Float(arc4random_uniform(256))/255.0,green:Float(arc4random_uniform(256))/255.0,blue:Float(arc4random_uniform(256))/255.0,alpha:1.0)
}
}
privateletkWaterCellID="kWaterCellID"
classViewController:UIViewController{
varcount:Int=20
overridefuncviewDidLoad(){
super.viewDidLoad()
//1.设置布局
letlayout=WaterfallLayout()
layout.minimumLineSpacing=10
layout.minimumInteritemSpacing=10
layout.sectionInset=UIEdgeInsets(top:10,left:10,bottom:10,right:10)
layout.dataSource=self
//2.创建UICollectionView
letcollectionView=UICollectionView(frame:view.bounds,collectionViewLayout:layout)
collectionView.www.wang027.comdataSource=self
collectionView.register(UICollectionViewCell.self,forCellWithReuseIdentifier:kWaterCellID)
view.addSubview(collectionView)
}
}
extensionViewController:UICollectionViewDataSource{
funccollectionView(_collectionView:UICollectionView,numberOfItemsInSectionsection:Int)->Int{
returncount
}
funccollectionView(_collectionView:UICollectionView,cellForItemAtindexPath:IndexPath)->UICollectionViewCell{
letcell=collectionView.dequeueReusableCell(withReuseIdentifier:kWaterCellID,for:indexPath)
cell.backgroundColor=UIColor.randomColor()
ifindexPath.item==count-1{
count+=20
collectionView.reloadData()
}
returncell
}
}
extensionViewController:WaterfallLayoutDataSource{
funcwaterfallLayout(_layout:WaterfallLayout,indexPath:IndexPath)->CGFloat{
returnCGFloat(arc4random_uniform(80)+100)
}
funcnumberOfColsInWaterfallLayout(_layout:WaterfallLayout)->Int{
return3
}
}
复制代码
封装自定义布局中的WaterfallLayout.swift代码如下:
复制代码
importUIKit
@objcprotocolWaterfallLayoutDataSource:class{
funcwaterfallLayout(_layout:WaterfallLayout,indexPath:IndexPath)->CGFloat
@objcoptionalfuncnumberOfColsInWaterfallLayout(_layout:WaterfallLayout)->Int
}
classWaterfallLayout:UICollectionViewFlowLayout{
//MARK:对外提供属性
weakvardataSource:WaterfallLayoutDataSource?
//MARK:私有属性
fileprivatelazyvarattrsArray:[UICollectionViewLayoutAttributes]=[UICollectionViewLayoutAttributes]()
fileprivatevartotalHeight:CGFloat=0
fileprivatelazyvarcolHeights:[CGFloat]={
letcols=self.dataSource?.numberOfColsInWaterfallLayout?(self)??2
varcolHeights=Array(repeating:self.sectionInset.top,count:cols)
returncolHeights
}()
fileprivatevarmaxH:CGFloat=0
fileprivatevarstartIndex=0
}
extensionWaterfallLayout{
//prepare准备所有Cell的布局样式
overridefuncprepare(){
super.prepare()
//0.获取item的个数
letitemCount=collectionView!.numberOfItems(inSection:0)
//1.获取列数
letcols=dataSource?.numberOfColsInWaterfallLayout?(self)??2
//2.计算Item的宽度
letitemW=(collectionView!.bounds.width-self.sectionInset.left-self.sectionInset.right-self.minimumInteritemSpacingCGFloat((cols-1)))/CGFloat(cols)
//3.计算所有的item的属性
foriinstartIndex.. //1.设置每一个Item位置相关的属性
letindexPath=IndexPath(item:i,section:0)
//2.根据位置创建Attributes属性
letattrs=UICollectionViewLayoutAttributes(forCellWith:indexPath)
//3.随机一个高度
guardletheight=dataSource?.waterfallLayout(self,indexPath:indexPath)else{
fatalError("请设置数据源,并且实现对应的数据源方法")
}
//4.取出最小列的位置
varminH=colHeights.min()!
letindex=colHeights.index(of:minH)!
minH=minH+height+minimumLineSpacing
colHeights[index]=minH
//5.设置item的属性
attrs.frame=CGRect(x:self.sectionInset.left+(self.minimumInteritemSpacing+itemW)CGFloat(index),y:minH-height-self.minimumLineSpacing,width:itemW,height:height)
attrsArray.append(attrs)
}
//4.记录最大值
maxH=colHeights.max()!
//5.给startIndex重新复制
startIndex=itemCount
}
}
extensionWaterfallLayout{
overridefunclayoutAttributesForElements(inrect:CGRect)->[UICollectionViewLayoutAttributes]?{
returnattrsArray
}
overridevarcollectionViewContentSize:CGSize{
returnCGSize(width:0,height:maxH+sectionInset.bottom-minimumLineSpacing)
}
}
|
|