转载自: http://blog.csdn.net/shengpeng3344/article/details/51673707
https://github.com/lxcid/LXReorderableCollectionViewFlowLayout
版权声明:本文为博主原创文章,未经博主允许不得转载。
UICollectionViewFlowLayout 流式布局我们常用,但他不支持横向排列并横向滑动,我们要求的是这样的效果
要实现这样的效果,我们只有自定义布局,自己设置cell的排列逻辑,笔者试过使用iOS 9的重排API,发现你如果重写layout,底层重排逻辑你没法重写,你也不知道UIKit工程师是怎样写的,所以采用完全自定义Collectionview,这里采用XWDragCellCollectionView(http://www.jianshu.com/p/8f0153ce17f9),逻辑都是一个思路,大家自己写也可以,稍作修改
1.首先我们需要建立一个layout类继承自UICollectionViewLayout,为什么不继承自UICollectionViewFlowLayout,因为UICollectionViewFlowLayout已经写好了逻辑,我们不清楚他是怎么写的,避免出问题,还是完全自定义
#import
@interface PagingCollectionViewLayout : UICollectionViewLayout
@property (nonatomic) CGFloat minimumLineSpacing; //行间距
@property (nonatomic) CGFloat minimumInteritemSpacing; //item间距
@property (nonatomic) CGSize itemSize; //item大小
@property (nonatomic) UIEdgeInsets sectionInset;
- (instancetype)init;
@end
2.在prepareLayout中计算行间距列间距,这个方法是每次要布局时都会调用,你可以在这里面设置默认参数
- (void)prepareLayout
{
[super prepareLayout]; //需调用父类方法
self.itemSize = CGSizeMake(70, 85);
self.sectionInset = UIEdgeInsetsMake(5, 10, 5, 10);
self.minimumLineSpacing = 1;
self.minimumInteritemSpacing = 1;
CGFloat itemWidth = self.itemSize.width;
CGFloat itemHeight = self.itemSize.height;
CGFloat width = self.collectionView.frame.size.width;
CGFloat height = self.collectionView.frame.size.height;
//计算真实item间距
CGFloat contentWidth = (width - self.sectionInset.left - self.sectionInset.right);
if (contentWidth >= (2*itemWidth+self.minimumInteritemSpacing)) { //如果列数大于2行
int m = (contentWidth-itemWidth)/(itemWidth+self.minimumInteritemSpacing);
_line = m+1;
int n = (int)(contentWidth-itemWidth)%(int)(itemWidth+self.minimumInteritemSpacing);
if (n > 0) {
double offset = ((contentWidth-itemWidth) - m*(itemWidth+self.minimumInteritemSpacing))/m;
itemSpacing = self.minimumInteritemSpacing + offset;
}else if (n == 0){
itemSpacing = self.minimumInteritemSpacing;
}
}else{ //如果列数为一行
itemSpacing = 0;
}
//计算行间距
CGFloat contentHeight = (height - self.sectionInset.top - self.sectionInset.bottom);
if (contentHeight >= (2*itemHeight+self.minimumLineSpacing)) { //如果行数大于2行
int m = (contentHeight-itemHeight)/(itemHeight+self.minimumLineSpacing);
_row = m+1;
int n = (int)(contentHeight-itemHeight)%(int)(itemHeight+self.minimumLineSpacing);
if (n > 0) {
double offset = ((contentHeight-itemHeight) - m*(itemHeight+self.minimumLineSpacing))/m;
lineSpacing = self.minimumLineSpacing + offset;
}else if (n == 0){
lineSpacing = self.minimumInteritemSpacing;
}
}else{ //如果行数数为一行
lineSpacing = 0;
}
int itemNumber = 0;
itemNumber = itemNumber + (int)[self.collectionView numberOfItemsInSection:0];
pageNumber = (itemNumber - 1)/(_row*_line) + 1;
}
- (BOOL)ShouldinvalidateLayoutForBoundsChange:(CGRect)newBounds{
return YES;
}
3.重写layoutAttributesForItemAtIndexPath:方法,这个方法返回的是cell的attribute,意思是cell的属性信息,包括位置大小形变等等。你可以在这里自定义cell的排序算法,随便怎么排列都行,但要有规律,不然重排会出问题。
static long pageNumber = 1;
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGRect frame;
frame.size = self.itemSize;
//下面计算每个cell的frame 可以自己定义
long number = _row * _line;
// printf("%ld\n",number);
long m = 0; //初始化 m p
long p = 0;
if (indexPath.item >= number) {
// NSLog(@"indexpath.item:%ld",indexPath.item);
p = indexPath.item/number; //计算页数不同时的左间距
// if ((p+1) > pageNumber) { //计算显示的页数
// pageNumber = p+1;
//
// }
// NSLog(@"%ld",p);
m = (indexPath.item%number)/_line;
}else{
m = indexPath.item/_line;
}
long n = indexPath.item%_line;
frame.origin = CGPointMake(n*self.itemSize.width+(n)*itemSpacing+self.sectionInset.left+(indexPath.section+p)*self.collectionView.frame.size.width,m*self.itemSize.height + (m)*lineSpacing+self.sectionInset.top);
// printf("%d(%f,%f)\n",indexPath.item,frame.origin.x,frame.origin.y);
attribute.frame = frame;
return attribute;
}
4.重写layoutAttributesForElementsInRect: 这个方法返回可见范围内的全部cell的attribute,cell的实时属性是由这个方法给的,意思就是说,例如你想cell移到屏幕中央时变大,就在这里写,然后替换原数组的attribute,返回array就行了。
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
NSMutableArray *tmpAttributes = [NSMutableArray new];
for (int j = 0; j < self.collectionView.numberOfSections; j ++) {
NSInteger count = [self.collectionView numberOfItemsInSection:j];
for (NSInteger i = 0; i < count; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:j];
[tmpAttributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
}
}
self.attributes = tmpAttributes;
return self.attributes;
}
5.此外还有cell的对准方格方法
- (CGPoint)targetContentOffsetForProposedContentOffset: (CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity //自动对齐到网格
{
//proposedContentOffset是没有对齐到网格时本来应该停下的位置
CGFloat offsetY = MAXFLOAT;
CGFloat offsetX = MAXFLOAT;
CGFloat horizontalCenter = proposedContentOffset.x + self.itemSize.width/2;
CGFloat verticalCenter = proposedContentOffset.y + self.itemSize.height/2;
CGRect targetRect = CGRectMake(0, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
NSArray* array = [super layoutAttributesForElementsInRect:targetRect];
//对当前屏幕中的UICollectionViewLayoutAttributes逐个与屏幕中心进行比较,找出最接近中心的一个
CGPoint offPoint = proposedContentOffset;
for (UICollectionViewLayoutAttributes* layoutAttributes in array) {
CGFloat itemHorizontalCenter = layoutAttributes.center.x;
CGFloat itemVerticalCenter = layoutAttributes.center.y;
if (ABS(itemHorizontalCenter - horizontalCenter) && (ABS(offsetX)>ABS(itemHorizontalCenter - horizontalCenter))) {
offsetX = itemHorizontalCenter - horizontalCenter;
offPoint = CGPointMake(itemHorizontalCenter, itemVerticalCenter);
}
if (ABS(itemVerticalCenter - verticalCenter) && (ABS(offsetY)>ABS(itemVerticalCenter - verticalCenter))) {
offsetY = itemHorizontalCenter - horizontalCenter;
offPoint = CGPointMake(itemHorizontalCenter, itemVerticalCenter);
}
}
return offPoint;
}
自定义layout后,我们需要建立一个collectionview,并使用这个layout,由于重排要我们自己定义,所以在collectionview中添加长按手势,用于拖动item并重排,重排逻辑在手势响应时间中写。
在自定义collectionview的.m文件中写:
/**
* 添加一个自定义的滑动手势
*/
- (void)xwp_addGesture{
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(xwp_longPressed:)];
longPress.minimumPressDuration = _minimumPressDuration; //最小长按时间
_longPressGesture = longPress;
[self addGestureRecognizer:longPress];
}
/**
* 监听手势的改变
*/
- (void)xwp_longPressed:(UILongPressGestureRecognizer *)longPressGesture{
if (longPressGesture.state == UIGestureRecognizerStateBegan) {
[self xwp_gestureBegan:longPressGesture]; //长按开始,隐藏cell并截图,添加截图UIView
}
if (longPressGesture.state == UIGestureRecognizerStateChanged) {
[self xwp_gestureChange:longPressGesture];//处理重排逻辑,数据处理,界面处理
}
if (longPressGesture.state == UIGestureRecognizerStateCancelled ||
longPressGesture.state == UIGestureRecognizerStateEnded){
[self xwp_gestureEndOrCancle:longPressGesture];//结束处理
}
}
还有很多,这里不贴了,详细代码见:github链接:https://github.com/shengpeng3344/PagingCollectionView
有问题留言