如图所示效果,根据字符长度自适应UICollectionViewCell
的大小,同时进行左对齐处理。
如何实现
继承UICollectionViewFlowLayout
创建子类,并实现相关的方法,如:
1、- (void)prepareLayout;
2、- (CGSize)collectionViewContentSize;
3、- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;
具体怎么编码实现
代码示例如下:
1、UICollectionViewFlowLayout
子类
.h文件
#import
NS_ASSUME_NONNULL_BEGIN
@protocol CollectionLayoutDelegate
/// 获取item高度
- (CGFloat)widthForItemIndexPath:(NSIndexPath *)indexPath AndCollectioinView:(UICollectionView *)collectionView;
@end
@interface CollectionLayout : UICollectionViewFlowLayout
@property (nonatomic, weak) iddelegate;
/// 视图宽
@property (nonatomic, assign) CGFloat layoutWidth;
/// 自适应collection的大小(默认NO)
@property (nonatomic, assign) BOOL autoContentSize;
@end
.m文件
#import "CollectionLayout.h"
@interface CollectionLayout ()
// 临时保存item的总宽度
@property (nonatomic, assign) CGFloat columnWidth;
// 记录一共有多少行
@property (nonatomic, assign) NSInteger columnNumber;
// 保存每一个item x y w h
@property (nonatomic, retain) NSMutableArray *arrForItemAtrributes;
// 保存item总数
@property (nonatomic,assign) NSUInteger numberOfItems;
// 保存每个item的X值
@property (nonatomic, assign) CGFloat xForItemOrigin;
// 保存每个item的Y值
@property (nonatomic, assign) CGFloat yForItemOrigin;
@end
@implementation CollectionLayout
// 准备布局
- (void)prepareLayout
{
[super prepareLayout];
//
self.columnWidth = self.sectionInset.left;
self.columnNumber = 0;
self.arrForItemAtrributes = [NSMutableArray array];
self.xForItemOrigin = self.sectionInset.left;
self.yForItemOrigin = self.sectionInset.top;
// 获取item的个数
self.numberOfItems = [self.collectionView numberOfItemsInSection:0];
/** 为每个item确定LayoutAttribute属性,同时将这些属性放入布局数组中 */
for (int i = 0; i < self.numberOfItems; i++) {
/** 确定每个Item的indexPath属性 */
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
/** 确定每个item的origin的x,y值 */
/** 确定每个Item的frame属性,同时确定了每个Item的LayoutAttribute,放入到了布局属性数组中 */
[self setFrame:indexPath];
}
}
// 计算contentView的大小
- (CGSize)collectionViewContentSize
{
// 获取collectionView的Size
CGSize contentSize = self.collectionView.frame.size;
// 最大高度+bottom
CGFloat height = self.sectionInset.top + (self.estimatedItemSize.height * (self.columnNumber + 1)) + (self.minimumLineSpacing * self.columnNumber) + self.sectionInset.bottom;
contentSize.height = height;
// 设置collectionView的大小自适应
if (self.autoContentSize) {
self.collectionView.frame = CGRectMake(self.collectionView.frame.origin.x, self.collectionView.frame.origin.y, contentSize.width, contentSize.height);
}
return contentSize;
}
// 返回每一个item的attribute
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
// 返回每一个item的Attribute
return self.arrForItemAtrributes;
}
// 设置属性和frame
- (void)setFrame:(NSIndexPath *)indexPath
{
// 设置Item LayoutAttribute 属性
UICollectionViewLayoutAttributes *layoutArr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
// 获取item的高
CGFloat itemWidth = 0;
if (_delegate && [_delegate respondsToSelector:@selector(widthForItemIndexPath:AndCollectioinView:)]) {
// 使用代理方法获取item的高
itemWidth = [_delegate widthForItemIndexPath:indexPath AndCollectioinView:self.collectionView];
}
//之前item的宽总和 + 当前item的宽 + 间距 < 屏幕总款
if (self.columnWidth + itemWidth + self.minimumInteritemSpacing < self.layoutWidth) {
// 设置x
self.xForItemOrigin = self.columnWidth;
self.columnWidth += itemWidth + self.minimumInteritemSpacing;
} else {
self.xForItemOrigin = self.sectionInset.left;
// 如果宽度超过屏幕从新计算宽度
self.columnWidth = self.sectionInset.left + itemWidth + self.minimumInteritemSpacing;
self.columnNumber++;
}
// 计算是第几行 乘以高度
self.yForItemOrigin = self.sectionInset.top + (self.estimatedItemSize.height + self.minimumLineSpacing) * self.columnNumber;
// 设置frame
layoutArr.frame = CGRectMake(self.xForItemOrigin, self.yForItemOrigin, itemWidth, self.estimatedItemSize.height);
// 放入数组
[self.arrForItemAtrributes addObject:layoutArr];
}
@end
2、UICollectionViewCell
子类
.h文件
#import
#import "TextModel.h"
static CGFloat cellOrigin = 20;
static CGFloat cellWidth = 50;
static CGFloat cellHeight = 32;
@interface CollectionCell : UICollectionViewCell
@property (nonatomic, strong) TextModel *model;
//
@property (nonatomic, copy) void (^itemClick)(void);
/// cell动态宽
+ (CGFloat)widthCell:(NSString *)text;
@end
.m文件
#import "CollectionCell.h"
@interface CollectionCell ()
@property (nonatomic, strong) UIButton *button;
@end
@implementation CollectionCell
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = UIColor.clearColor;
//
[self setUI];
}
return self;
}
#pragma mark - 视图
- (void)setUI
{
self.button = UIButtonInitializeWithTitle(self.contentView, CGRectZero, 0, nil, nil, kColorBlack, kColorBlue, kColorBlue, UIFontAutoSize(14), self, @selector(selectedButtonClick:));
self.button.viewRadius(self.height / 2).viewBorder(1, kColorLineBorder);
//
[self.button mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.bottom.right.mas_equalTo(0);
}];
}
- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes{
UICollectionViewLayoutAttributes *attributes = [super preferredLayoutAttributesFittingAttributes:layoutAttributes];
CGRect rect = self.button.frame;
rect.size.width = self.model.nameWidth;
rect.size.height =cellHeight;
attributes.frame = rect;
return attributes;
}
#pragma mark - 交互
- (void)selectedButtonClick:(UIButton *)button
{
button.selected = YES;
button.userInteractionEnabled = NO;
self.button.viewBorder(1, kColorBlue);
//
self.model.selected = button.selected;
if (self.itemClick) {
self.itemClick();
}
}
/// cell动态宽
+ (CGFloat)widthCell:(NSString *)text
{
CGFloat width = [text widthForFont:UIFontAutoSize(14)];
width += 20;
width = (width <= 50 ? 50 : width);
return width;
}
#pragma mark - setter/getter
- (void)setModel:(TextModel *)model
{
_model = model;
//
CGFloat width = _model.nameWidth;
if (width <= 0.0) {
width = [[self class] widthCell:_model.name];
model.nameWidth = width;
}
//
NSString *text = _model.name;
[self.button setTitle:text forState:UIControlStateNormal];
if (_model.selected) {
self.button.selected = YES;
self.button.userInteractionEnabled = NO;
self.button.viewBorder(kLineBorderWidth, kColorBlue);
} else {
self.button.selected = NO;
self.button.userInteractionEnabled = YES;
self.button.viewBorder(kLineBorderWidth, kColorLineBorder);
}
DLog(@"cell width = %.2f", width);
}
@end
3、UICollectionView
子类
.h文件
#import
#import "TextModel.h"
#import "CollectionCell.h"
NS_ASSUME_NONNULL_BEGIN
@interface Collection : UICollectionView
@property (nonatomic, strong) NSArray *array;
@property (nonatomic, copy) void (^cellClick)(NSIndexPath *indexPath);
@end
.m文件
#import "Collection.h"
@interface Collection ()
@property (nonatomic, strong) NSIndexPath *previousIndexPath;
@end
@implementation Collection
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
{
self = [super initWithFrame:frame collectionViewLayout:layout];
if (self) {
self.backgroundColor = UIColor.clearColor;
//
[self registerClass:CollectionCell.class forCellWithReuseIdentifier:@"CollectionCell"];
self.delegate = self;
self.dataSource = self;
}
return self;
}
#pragma mark - 交互
#pragma mark - UICollectionViewDelegate, UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.array.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CollectionCell" forIndexPath:indexPath];
TextModel *model = self.array[indexPath.row];
cell.model = model;
kSelfWeak;
cell.itemClick = ^{
if (weakSelf.previousIndexPath) {
if ([weakSelf.previousIndexPath isEqual:indexPath]) {
return ;
}
TextModel *previousModel = weakSelf.array[weakSelf.previousIndexPath.row];
previousModel.selected = NO;
[collectionView reloadItemsAtIndexPaths:@[weakSelf.previousIndexPath]];
}
weakSelf.previousIndexPath = indexPath;
//
if (weakSelf.cellClick) {
weakSelf.cellClick(indexPath);
}
};
return cell;
}
- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
#pragma mark - setter
- (void)setArray:(NSArray *)array
{
_array = array;
//
[_array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
TextModel *model = (TextModel *)obj;
if (model.selected) {
self.previousIndexPath = [NSIndexPath indexPathForRow:idx inSection:0];
*stop = YES;
}
}];
[self reloadData];
}
@end
4、实现
// 实例化
CollectionLayout *layout = [[CollectionLayout alloc] init];
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
layout.layoutWidth = self.width;
layout.delegate = self;
layout.autoContentSize = YES;
layout.minimumLineSpacing = cellOrigin / 2;
layout.minimumInteritemSpacing =cellOrigin / 2;
layout.sectionInset = UIEdgeInsetsMake(cellOrigin, cellOrigin, cellOrigin,cellOrigin);
layout.estimatedItemSize = CGSizeMake(cellWidth, cellHeight);
//
_collectionView = [[BCollection alloc] initWithFrame:CGRectMake(0, 0, self.width, (self.height / 2)) collectionViewLayout:layout];
[self addSubview:_collectionView];
_collectionView.backgroundColor = kColorWhite;
// 回调
kSelfWeak;
_collectionView.cellClick = ^(NSIndexPath * _Nonnull indexPath) {
if (weakSelf.selecteClick) {
weakSelf.selecteClick(indexPath);
}
};
// 赋值
collectionView.array = _array;
// layout代理方法
- (CGFloat)widthForItemIndexPath:(NSIndexPath *)indexPath AndCollectioinView:(UICollectionView *)collectionView
{
TextModel *model = self.array[indexPath.row];
CGFloat width = model.nameWidth;
if (width <= 0) {
width = [CollectionCell widthCell:model.name];
model.nameWidth = width;
}
return width;
}
编码注意事项
1、layout必须实现属性:
layoutWidth
,minimumLineSpacing
,minimumInteritemSpacing
,sectionInset
,estimatedItemSize
,delegate
。主要用于设置cell的大小,内边距和间距。
2、cell必须实现方法- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
,用于改变cell的大小。