最近一直在做项目,没什么时间发博客.现在有空了,整理一下这段时间项目里的一些实用技术.如果有问题欢迎随时找我交流.
iOS瀑布流,实现的是对于不同尺寸控件的合理布局
举个例子:(ONE一个的往期列表)
通过collectionView利用自定义的layout进行布局,cell进行自适应.我个人觉得这个效果特别适合一个这个APP.
下面来看一下具体的代码实现:
首先,看一下数据内容:(这里我找了一个本地的json文件当做数据源)
{
"thumbURL":"http://amuse.nen.com.cn/imagelist/11/21/9as70n3ir61b.jpg",
"width": 482,
"height": 480
}
代码内容:
MyLayOut.h
#import <UIKit/UIKit.h>
@protocol MyLayOutDelegate <NSObject>
/**
* 获取item的高度
*
* @param indexPath 下标
*
* @return item高度
*/
- (CGFloat)heightForItemAtIndexPath:(NSIndexPath *)indexPath;
@end
@interface MyLayOut : UICollectionViewLayout
/**
* 单元格尺寸
*/
@property (nonatomic, assign) CGSize itemSize;
/**
* 列数
*/
@property (nonatomic, assign) NSInteger numberOfColumns;
/**
* 内边距
*/
@property (nonatomic, assign) UIEdgeInsets sectionInSet;
/**
* item间隔
*/
@property (nonatomic, assign) CGFloat ItemSpacing;
/**
* 代理人属性
*/
@property (nonatomic, assign) id<MyLayOutDelegate>delegate;
@end
MyLayOut.m
#import "MyLayOut.h"
@interface MyLayOut ()
/**
* 列高
*/
@property (nonatomic, strong) NSMutableArray *columnsHeights;
/**
* item的数量
*/
@property (nonatomic, assign) NSInteger numberOfItems;
/**
* 存放每个item的位置信息的数组
*/
@property (nonatomic, strong) NSMutableArray *itemAttributes;
/**
* 临时存储当前item的x值
*/
@property (nonatomic, assign) CGFloat item_X;
/**
* 临时存储当前item的Y值
*/
@property (nonatomic, assign) CGFloat item_Y;
/**
* 最矮列下标
*/
@property (nonatomic, assign) NSInteger shortestIndex;
@end
@implementation MyLayOut
#pragma mark -------------------- 懒加载
- (NSMutableArray *)columnsHeights
{
if (!_columnsHeights)
{
self.columnsHeights = [NSMutableArray array];
}
return _columnsHeights;
}
- (NSMutableArray *)itemAttributes
{
if (!_itemAttributes)
{
self.itemAttributes = [NSMutableArray array];
}
return _itemAttributes;
}
#pragma mark -- 获取最矮列的下标
- (NSInteger)getShortestColumnIndex
{
//最矮列下标
NSInteger shortestIndex = 0;
//column高度
CGFloat shortestHeight = MAXFLOAT;
//遍历高度数组获得最矮列下标
for (NSInteger i = 0; i < self.numberOfColumns; i ++)
{
CGFloat currentHeight = [[self.columnsHeights objectAtIndex:i] floatValue];
if (currentHeight < shortestHeight)
{
shortestHeight = currentHeight;
shortestIndex = i;
}
}
return shortestIndex;
}
#pragma mark -- 获取最高列的下标
- (NSInteger)getHighestColumnIndex
{
//最高列下标
NSInteger highestIndex = 0;
//column高度
CGFloat highestHeight = 0;
//遍历高度数组获得最高列下标
for (NSInteger i = 0; i < self.numberOfColumns; i ++)
{
CGFloat currentHeight = [[self.columnsHeights objectAtIndex:i] floatValue];
if (currentHeight > highestHeight)
{
highestHeight = currentHeight;
highestIndex = i;
}
}
return highestIndex;
}
#pragma mark -- 添加顶部内边距的值
- (void)addTopValueForColumns
{
for (NSInteger i = 0; i < self.numberOfColumns; i ++)
{
self.columnsHeights[i] = @(self.sectionInSet.top);
}
}
#pragma mark -- 计算每个item的X和Y
- (void)getOriginInShortestColumn
{
//获取最矮列下标
self.shortestIndex = [self getShortestColumnIndex];
//获取最矮列的高度
CGFloat shortestHeight = [[self.columnsHeights objectAtIndex:self.shortestIndex] floatValue];
//设置item的X
self.item_X = self.sectionInSet.left + (self.itemSize.width + self.ItemSpacing) * self.shortestIndex;
//设置item的Y
self.item_Y = shortestHeight + self.ItemSpacing;
}
#pragma mark -- 计算width和height --> 生成frame
- (void)setFrame:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *layOutAttribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
//存放item的高度
CGFloat itemHeight = 0;
if (self.delegate && [self.delegate respondsToSelector:@selector(heightForItemAtIndexPath:)])
{
itemHeight = [self.delegate heightForItemAtIndexPath:indexPath];
}
layOutAttribute.frame = CGRectMake(_item_X, _item_Y, self.itemSize.width, itemHeight);
//将位置信息加入数组
[self.itemAttributes addObject:layOutAttribute];
//更新当前列的高度
self.columnsHeights[_shortestIndex] = @(self.item_Y + itemHeight);
}
#pragma mark -- 重写父类布局方法
- (void)prepareLayout
{
[super prepareLayout];
//为高度数组添加上边距
[self addTopValueForColumns];
//获取item个数
self.numberOfItems = [self.collectionView numberOfItemsInSection:0];
//循环布局
for (NSInteger i = 0; i < self.numberOfItems; i ++)
{
//计算item的X和Y
[self getOriginInShortestColumn];
//生成indexPath
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
//生成frame
[self setFrame:indexPath];
}
}
#pragma mark -- 获取contentView尺寸
- (CGSize)collectionViewContentSize
{
//获取最高列下标
NSInteger highestIndex = [self getHighestColumnIndex];
//获取最高列高度
CGFloat highestHeight = [[self.columnsHeights objectAtIndex:highestIndex] floatValue];
//构造contentView的size
CGSize size = self.collectionView.frame.size;
//修改高度
size.height = highestHeight + self.sectionInSet.bottom;
//返回size
return size;
}
#pragma mark -- 返回位置信息数组
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.itemAttributes;
}
@end
ViewController.m
#import "ViewController.h"
#import "Model.h"
#import "MyLayOut.h"
#import "ImageCell.h"
#import "UIImageView+WebCache.h"
@interface ViewController ()<MyLayOutDelegate, UICollectionViewDelegate, UICollectionViewDataSource>
/**
* 数据源数组
*/
@property (nonatomic, strong) NSMutableArray *dataSource;
/**
* 集合视图
*/
@property (nonatomic, strong) UICollectionView *collectionView;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self getData];
[self createCollectionView];
}
#pragma mark -------------------- 请求数据
- (void)getData
{
self.dataSource = [NSMutableArray array];
NSData *data = [NSData dataWithContentsOfFile:@"/Users/dllo/Desktop/简书/MyFlowLayOut/MyFlowLayOut/Data.json"];
NSArray *array = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
for (NSDictionary *dic in array)
{
Model *model = [[Model alloc] init];
[model setValuesForKeysWithDictionary:dic];
[self.dataSource addObject:model];
}
}
#pragma mark -------------------- 创建collectionView
- (void)createCollectionView
{
MyLayOut *layOut = [[MyLayOut alloc] init];
layOut.delegate = self;
layOut.itemSize = CGSizeMake((self.view.frame.size.width - 40) / 3.0, (self.view.frame.size.width - 40) / 3.0);
layOut.ItemSpacing = 10;
layOut.sectionInSet = UIEdgeInsetsMake(10, 10, 10, 10);
layOut.numberOfColumns = 3;
self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layOut];
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
self.collectionView.backgroundColor = [UIColor colorWithRed:0.52 green:0.74 blue:0.96 alpha:1.00];
[self.view addSubview:_collectionView];
//注册cell:
[self.collectionView registerClass:[ImageCell class] forCellWithReuseIdentifier:@"imageCell"];
}
#pragma mark -------------------- 实现自定义协议方法
- (CGFloat)heightForItemAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat width = ([UIScreen mainScreen].bounds.size.width - 40) / 3.0;
Model *model = [self.dataSource objectAtIndex:indexPath.row];
CGFloat height = (model.height / model.width) * width;
return height;
}
#pragma mark -------------------- 实现collectionView协议方法
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.dataSource.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
ImageCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"imageCell" forIndexPath:indexPath];
cell.backgroundColor = [UIColor lightGrayColor];
cell.layer.cornerRadius = 5;
cell.photoImageView.layer.cornerRadius = 5;
cell.photoImageView.clipsToBounds = YES;
cell.photoImageView.contentMode = UIViewContentModeScaleAspectFit;
[cell.photoImageView sd_setImageWithURL:[NSURL URLWithString:[[self.dataSource objectAtIndex:indexPath.row] thumbURL]]];
return cell;
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
实现效果如下:
在写的过程中也遇到了很多问题,因为对于layout
的实现我也不是很熟悉,还需要进一步调研,希望大家都能共同进步!