UICollectionView详解五:瀑布流
前⾯四个章节,我已经详细的讲解了UICollectionView的使⽤,这⼀节,我⽤⼀个⾮常实⽤的例⼦“瀑布流”来进⼀步说明UICollectionView的强⼤作⽤。
先分析⼀下瀑布流的特点:
1. 所有item的宽度是⼀致的。
2. 所有item应该是等⽐例缩放的。
3. 所有item的⾼度应该是通过实际宽度与缩放⽐例计算⽽得出的。
4. 要保证每⼀列的底部的y值均匀分布,不能偏差很⼤。
5. 瀑布流不是常规的流式布局,所以应该使⽤UICollectionViewLayout,对UICollectionViewLayout不明⽩的,请参考我前⾯写的章节,请。
下⾯是运⾏效果图:
1. 竖屏
2. 横屏
好的,下⾯,我们来⼀步步的实现这个效果。
1.准备数据源(我使⽤的是plist⽂件,实际开发中游可能是json数据,不过是差不多的):
对数据源的说明:
注意:需要服务器端提供图⽚的宽度和⾼度的信息(h,w两个值)。如果我们不把宽度和⾼度信息放在数据源中,那么当图⽚信息获取后,我们⾃⼰还要在前端⾃⼰计算宽度和⾼度。在⽹络不好的情况下,有的图⽚也许长时间加载不到,那么我们就不知道怎么去布局了。如果提供了图⽚的宽度和⾼度信息,就算图⽚没有加载到,但是宽度和⾼度信息是可以获取到的,这个时候,我们可以放置占位图⽚,等图⽚加载完毕后,再替换掉占位图⽚。
2. 建⽴对应的模型
@interface LFShop : NSObject
/*图⽚的宽度*/
@property (nonatomic,assign) CGFloat w;
/*图⽚的⾼度*/
@property (nonatomic,assign) CGFloat h;
/*图⽚的url*/
@property (nonatomic,copy) NSString *img;
/*图⽚的价格信息*/
@property (nonatomic,copy) NSString *price;
@end
3. ⾃定义UICollectionViewCell,⽤来显⽰最终的图⽚信息
@class LFShop;
@interface LFWaterFlowCell : UICollectionViewCell
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIButton *priceBtn;
@property (nonatomic,strong) LFShop *shop;
@end
@implementation LFWaterFlowCell
-(void)setShop:(LFShop *)shop {
_shop = shop;
[self.imageView sd_setImageWithURL:[NSURL URLWithString:shop.img] placeholderImage:[UIImage imageNamed:@"placeholder.jpg"] options:SDWebImageR
[self.priceBtn setTitle:shop.price forState:UIControlStateNormal];
}
@end
xib结构图
view ui框架
4. 在控制器ViewController.m中初始化UICollectionView,及设置数据源⽅法
@interface ViewController ()<UICollectionViewDataSource,UICollectionViewDelegate,LFWaterFlowLayoutDelegate>
@property (nonatomic,strong) NSMutableArray *shops;
@property (nonatomic,weak) LFWaterFlowLayout *layout;
@property (nonatomic,weak) UICollectionView *collectionView;
@property (nonatomic,assign,getter=isLoadRotate) BOOL loadRotate;
@end
static NSString *const identifer = @"LFWaterFlowCell";
@implementation ViewController
#pragma mark - Lazy Load
-(NSMutableArray *)shops {
if (!_shops) {
NSArray *defaultArray = [LFShop objectArrayWithFilename:@"2.plist"];
_shops = [NSMutableArray array];
[_shops addObjectsFromArray:defaultArray];
}
return _shops;
}
#pragma mark - init
- (void)viewDidLoad {
[super viewDidLoad];
[self collectionViewInit];
}
- (void)collectionViewInit {
LFWaterFlowLayout *layout = [[LFWaterFlowLayout alloc] init];
layout.delegate = self;
self.layout = layout;
//layout.insets = UIEdgeInsetsMake(20, 20, 20, 20);
//unt = 4;
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
collectionView.dataSource = self;
collectionView.delegate = self;
collectionView.backgroundColor = [UIColor darkGrayColor];
[self.view addSubview:collectionView];
// autolayout全屏幕显⽰
[collectionView autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsZero];
[collectionView registerNib:[UINib nibWithNibName:@"LFWaterFlowCell" bundle:nil] forCellWithReuseIdentifier:identifer];
}
#pragma mark - UICollectionView
// Datasource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return  unt;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {    LFWaterFlowCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifer forIndexPath:indexPath];
cell.shop = self.shops[indexPath.item];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
LFShop *shop = self.shops[indexPath.item];
NSLog(@"Item Price:%@",shop.price);
}
@end
代码中,⼤家可以看到,我定义了⼀个LFWaterFlowLayout,它就是⽤来对UICollectionView进⾏布局的。
5. 在看具体代码之前,我们先看看瀑布流的具体结构⽰意图。
LFWaterFlowLayout.h对应的定义代码如下:
@class LFWaterFlowLayout;
@protocol  LFWaterFlowLayoutDelegate <NSObject>
/*通过代理获得每个cell的⾼度(之所以⽤代理取得⾼度的值,就是为了解耦,这⾥定义的LFWaterFlowLayout不依赖与任务模型数据)*/
- (CGFloat)waterFlowLayout:(LFWaterFlowLayout *)waterFlowLayout heightForWidth:(CGFloat)width atIndexPath:(NSIndexPath *)indexPath;
@end
@interface LFWaterFlowLayout : UICollectionViewLayout
/*cell的列间距*/
@property (nonatomic,assign) CGFloat columnMargin;
/*cell的⾏间距*/
@property (nonatomic,assign) CGFloat rowMargin;
/*cell的top,right,bottom,left间距*/
@property (nonatomic,assign) UIEdgeInsets insets;
/*显⽰多少列*/
@property (nonatomic,assign) NSInteger count;
@property (nonatomic,assign) id<LFWaterFlowLayoutDelegate> delegate;
@end
LFWaterFlowLayout.m  中具体的实现代码:
这⾥⾯的难点就是怎么计算每⼀个cell所在的位置。主要代码在layoutAttributesForItemAtIndexPath ⽅法中。代码实现流程图:
@interface LFWaterFlowLayout()
/* Key: 第⼏列; Value: 保存每列的cell的底部y值 */
@property (nonatomic,strong) NSMutableDictionary *cellInfo;
@end
@implementation LFWaterFlowLayout
#pragma mark - 初始化属性
- (instancetype)init {
self = [super init];
if (self) {
self.insets = UIEdgeInsetsMake(10, 10, 10, 10);

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。