配色: 字号:
iOS瀑布流实现(Swift)
2016-12-03 | 阅:  转:  |  分享 
  
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)

}

}

献花(0)
+1
(本文系thedust79首藏)