iOS 横向排列数据的UICollectionview

转载自: 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

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;



}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

自定义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];//结束处理
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

还有很多,这里不贴了,详细代码见:github链接:https://github.com/shengpeng3344/PagingCollectionView

有问题留言


你可能感兴趣的:(iOS 横向排列数据的UICollectionview)