最近在开发软件的时候被产品经理要求,要让UICollectionView上面的cell之间的距离要被固定,但是cell得宽度不一定,所以一行有几个cell其实不固定,所以其实cell之间的间距不一定,cell之间最终距离不是你设置的,而是由系统把一行中cell宽度减去,剩下的空白格平均分配的.所以我一开始写出来的效果看起来不是很规则

所以喽,产品经理不同意这样,说看起来不是很规则,然后只能去网上找,做种找到了别人的一些例子,最终弄出来一个貌似还能看的规则。

最近需求还没下来,所以把很久之前的这个代码给重新折腾了一下,支持靠左,居中,靠右,等间距对齐。整体逻辑也比之前清晰了一些。



#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger,AlignType){
AlignWithLeft,
AlignWithCenter,
AlignWithRight
};
@interface JYEqualCellSpaceFlowLayout : UICollectionViewFlowLayout
//两个Cell之间的距离
@property (nonatomic,assign)CGFloat betweenOfCell;
//cell对齐方式
@property (nonatomic,assign)AlignType cellType;
-(instancetype)initWthType : (AlignType)cellType;
//全能初始化方法 其他方式初始化最终都会�走到这里
-(instancetype)initWithType:(AlignType) cellType betweenOfCell:(CGFloat)betweenOfCell;
@end
#import "JYEqualCellSpaceFlowLayout.h"
@interface JYEqualCellSpaceFlowLayout(){
//在居中对齐的时候需要知道这行所有cell的宽度总和
CGFloat _sumCellWidth ;
}
@end
@implementation JYEqualCellSpaceFlowLayout
-(instancetype)init{
return [self initWithType:AlignWithCenter betweenOfCell:5.0];
}
-(void)setBetweenOfCell:(CGFloat)betweenOfCell{
_betweenOfCell = betweenOfCell;
self.minimumInteritemSpacing = betweenOfCell;
}
-(instancetype)initWthType:(AlignType)cellType{
return [self initWithType:cellType betweenOfCell:5.0];
}
-(instancetype)initWithType:(AlignType)cellType betweenOfCell:(CGFloat)betweenOfCell{
self = [super init];
if (self){
self.scrollDirection = UICollectionViewScrollDirectionVertical;
self.minimumLineSpacing = 5;
self.minimumInteritemSpacing = 5;
self.sectionInset = UIEdgeInsetsMake(5, 5, 5, 5);
_betweenOfCell = betweenOfCell;
_cellType = cellType;
}
return self;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray * layoutAttributes_t = [super layoutAttributesForElementsInRect:rect];
NSArray * layoutAttributes = [[NSArray alloc]initWithArray:layoutAttributes_t copyItems:YES];
//用来临时存放一行的Cell数组
NSMutableArray * layoutAttributesTemp = [[NSMutableArray alloc]init];
for (NSUInteger index = 0; index < layoutAttributes.count ; index++) {
UICollectionViewLayoutAttributes *currentAttr = layoutAttributes[index]; // 当前cell的位置信息
UICollectionViewLayoutAttributes *previousAttr = index == 0 ? nil : layoutAttributes[index-1]; // 上一个cell 的位置信
UICollectionViewLayoutAttributes *nextAttr = index + 1 == layoutAttributes.count ?
nil : layoutAttributes[index+1];//下一个cell 位置信息
//加入临时数组
[layoutAttributesTemp addObject:currentAttr];
_sumCellWidth += currentAttr.frame.size.width;
CGFloat previousY = previousAttr == nil ? 0 : CGRectGetMaxY(previousAttr.frame);
CGFloat currentY = CGRectGetMaxY(currentAttr.frame);
CGFloat nextY = nextAttr == nil ? 0 : CGRectGetMaxY(nextAttr.frame);
//如果当前cell是单独一行
if (currentY != previousY && currentY != nextY){
if ([currentAttr.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
[layoutAttributesTemp removeAllObjects];
_sumCellWidth = 0.0;
}else if ([currentAttr.representedElementKind isEqualToString:UICollectionElementKindSectionFooter]){
[layoutAttributesTemp removeAllObjects];
_sumCellWidth = 0.0;
}else{
[self setCellFrameWith:layoutAttributesTemp];
}
}
//如果下一个cell在本行,这开始调整Frame位置
else if( currentY != nextY) {
[self setCellFrameWith:layoutAttributesTemp];
}
}
return layoutAttributes;
}
//调整属于同一行的cell的位置frame
-(void)setCellFrameWith:(NSMutableArray*)layoutAttributes{
CGFloat nowWidth = 0.0;
switch (_cellType) {
case AlignWithLeft:
nowWidth = self.sectionInset.left;
for (UICollectionViewLayoutAttributes * attributes in layoutAttributes) {
CGRect nowFrame = attributes.frame;
nowFrame.origin.x = nowWidth;
attributes.frame = nowFrame;
nowWidth += nowFrame.size.width + self.betweenOfCell;
}
_sumCellWidth = 0.0;
[layoutAttributes removeAllObjects];
break;
case AlignWithCenter:
nowWidth = (self.collectionView.frame.size.width - _sumCellWidth - ((layoutAttributes.count - 1) * _betweenOfCell)) / 2;
for (UICollectionViewLayoutAttributes * attributes in layoutAttributes) {
CGRect nowFrame = attributes.frame;
nowFrame.origin.x = nowWidth;
attributes.frame = nowFrame;
nowWidth += nowFrame.size.width + self.betweenOfCell;
}
_sumCellWidth = 0.0;
[layoutAttributes removeAllObjects];
break;
case AlignWithRight:
nowWidth = self.collectionView.frame.size.width - self.sectionInset.right;
for (NSInteger index = layoutAttributes.count - 1 ; index >= 0 ; index-- ) {
UICollectionViewLayoutAttributes * attributes = layoutAttributes[index];
CGRect nowFrame = attributes.frame;
nowFrame.origin.x = nowWidth - nowFrame.size.width;
attributes.frame = nowFrame;
nowWidth = nowWidth - nowFrame.size.width - _betweenOfCell;
}
_sumCellWidth = 0.0;
[layoutAttributes removeAllObjects];
break;
}
}
@end
OC版本的Demo在这里
------------------------------------------------------分界线-----------------------------------------------------
下面是Swift版本的代码
import UIKit
enum AlignType : NSInteger {
case left = 0
case center = 1
case right = 2
}
class JYEqualCellSpaceFlowLayout: UICollectionViewFlowLayout {
//两个Cell之间的距离
var betweenOfCell : CGFloat{
didSet{
self.minimumInteritemSpacing = betweenOfCell
}
}
//cell对齐方式
var cellType : AlignType = AlignType.center
//在居中对齐的时候需要知道这行所有cell的宽度总和
var sumCellWidth : CGFloat = 0.0
override init() {
betweenOfCell = 5.0
super.init()
scrollDirection = UICollectionViewScrollDirection.vertical
minimumLineSpacing = 5
sectionInset = UIEdgeInsetsMake(5, 5, 5, 5)
}
convenience init(_ cellType:AlignType){
self.init()
self.cellType = cellType
}
convenience init(_ cellType: AlignType, _ betweenOfCell: CGFloat){
self.init()
self.cellType = cellType
self.betweenOfCell = betweenOfCell
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let layoutAttributes_super : [UICollectionViewLayoutAttributes] = super.layoutAttributesForElements(in: rect) ?? [UICollectionViewLayoutAttributes]()
let layoutAttributes:[UICollectionViewLayoutAttributes] = NSArray(array: layoutAttributes_super, copyItems:true)as! [UICollectionViewLayoutAttributes]
var layoutAttributes_t : [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]()
for index in 0..<layoutAttributes.count{
let currentAttr = layoutAttributes[index]
let previousAttr = index == 0 ? nil : layoutAttributes[index-1]
let nextAttr = index + 1 == layoutAttributes.count ?
nil : layoutAttributes[index+1]
layoutAttributes_t.append(currentAttr)
sumCellWidth += currentAttr.frame.size.width
let previousY :CGFloat = previousAttr == nil ? 0 : previousAttr!.frame.maxY
let currentY :CGFloat = currentAttr.frame.maxY
let nextY:CGFloat = nextAttr == nil ? 0 : nextAttr!.frame.maxY
if currentY != previousY && currentY != nextY{
if currentAttr.representedElementKind == UICollectionElementKindSectionHeader{
layoutAttributes_t.removeAll()
sumCellWidth = 0.0
}else if currentAttr.representedElementKind == UICollectionElementKindSectionFooter{
layoutAttributes_t.removeAll()
sumCellWidth = 0.0
}else{
self.setCellFrame(with: layoutAttributes_t)
layoutAttributes_t.removeAll()
sumCellWidth = 0.0
}
}else if currentY != nextY{
self.setCellFrame(with: layoutAttributes_t)
layoutAttributes_t.removeAll()
sumCellWidth = 0.0
}
}
return layoutAttributes
}
/// 调整Cell的Frame
///
/// - Parameter layoutAttributes: layoutAttribute 数组
func setCellFrame(with layoutAttributes : [UICollectionViewLayoutAttributes]){
var nowWidth : CGFloat = 0.0
switch cellType {
case AlignType.left:
nowWidth = self.sectionInset.left
for attributes in layoutAttributes{
var nowFrame = attributes.frame
nowFrame.origin.x = nowWidth
attributes.frame = nowFrame
nowWidth += nowFrame.size.width + self.betweenOfCell
}
break;
case AlignType.center:
nowWidth = (self.collectionView!.frame.size.width - sumCellWidth - (CGFloat(layoutAttributes.count - 1) * betweenOfCell)) / 2
for attributes in layoutAttributes{
var nowFrame = attributes.frame
nowFrame.origin.x = nowWidth
attributes.frame = nowFrame
nowWidth += nowFrame.size.width + self.betweenOfCell
}
break;
case AlignType.right:
nowWidth = self.collectionView!.frame.size.width - self.sectionInset.right
for var index in 0 ..< layoutAttributes.count{
index = layoutAttributes.count - 1 - index
let attributes = layoutAttributes[index]
var nowFrame = attributes.frame
nowFrame.origin.x = nowWidth - nowFrame.size.width
attributes.frame = nowFrame
nowWidth = nowWidth - nowFrame.size.width - betweenOfCell
}
break;
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Swift版本Demo在这里
网友评论
太66666666666666666666
太66666666666666666666
太66666666666666666666
太66666666666666666666
太66666666666666666666
这句应该是两个cell之间有一个_betweenOfCell吧。应为(layoutAttributes.count-1) * _betweenOfCell 不然间距设置大的情况下会导致左边缘的cell 出屏幕
cell数量减少会崩溃,增加要刷新两次才会出现。
//根据cell的Y轴位置来判断cell是否是单独一行
if curY > preY&&curY < nextY{
print(curAttr.representedElementKind)//在这个位置加上这句话 看看有没有输出UICollectionElementKindSectionHeader
}
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
for (int i=1; i < attributes.count; i++) {
UICollectionViewLayoutAttributes *curAttr = attributes[i]; // 当前cell的位置信息
UICollectionViewLayoutAttributes *preAttr = attributes[i-1]; // 下一个cell 的位置信息
// 下面这块代码是对于一行只有一个cell进行位置调整
UICollectionViewLayoutAttributes *nextAttr = nil;//上一个cell 位置信息
if (i+1 < attributes.count) {
nextAttr = attributes[i+1];
}
if (nextAttr != nil){
CGFloat preY = CGRectGetMaxY(preAttr.frame);
CGFloat curY = CGRectGetMaxY(curAttr.frame);
CGFloat nextY = CGRectGetMaxY(nextAttr.frame);
//根据cell的Y轴位置来判断cell是否是单独一行
if (curY > preY&&curY < nextY) {
//这个判断方式也会对区头进行判断 如果是区头则X轴还是从0开始
if ([curAttr.representedElementKind isEqualToString:@"UICollectionElementKindSectionHeader"]) {
CGRect frame = curAttr.frame;
frame.origin.x = 0;
curAttr.frame = frame;
} else {
//单独一行的cell的X轴从5开始
CGRect frame = curAttr.frame;
frame.origin.x = 5;
curAttr.frame = frame;
}
}
}
//下面是对一行多个cell的间距进行调整
CGFloat origin = CGRectGetMaxX(preAttr.frame);
CGFloat targetX = origin + self.minimumInteritemSpacing;
if (CGRectGetMinX(curAttr.frame) > targetX){
//如果下一个cell换行了则不进行调整
if (targetX + CGRectGetWidth(curAttr.frame) < self.collectionViewContentSize.width) {
CGRect frame = curAttr.frame;
frame.origin.x = targetX;
curAttr.frame = frame;
}
}
}
return attributes;
}
if (i == 1)
{
if ( preY != currentY )
{
CGRect frame = preLayoutAttribute.frame;
frame.origin.x = 0;
preLayoutAttribute.frame = frame;
}
}
感觉如果完善点,还得处理没有 header 和 footer 的情况。哈哈