(修正)分享一个卡片式布局的collectionView

作者: ZYiDa | 来源:发表于2017-08-17 16:25 被阅读147次

    老规矩,先看效果图

    QQ20170817-152627.gif
    第一步,自定义ZLFlowLayout继承于UICollectionViewFlowLayout

    具体代码:

    #import "ZLFlowLayout.h"
    #import "ZLFlowLayoutConstants.h"
    
    @implementation ZLFlowLayout
    
    
    - (instancetype)init
    {
        self = [super init];
        if (self)
        {
            self.minimumLineSpacing = 15.0f;
            self.itemSize = CGSizeMake(SCREEN_WIDTH - LEFT_OFFSET*2, (SCREEN_WIDTH - LEFT_OFFSET*2)/0.618f);
            self.scrollDirection = UICollectionViewScrollDirectionHorizontal;//设置为水平滑动
            self.sectionInset = UIEdgeInsetsMake(64, LEFT_OFFSET, 0, LEFT_OFFSET);
        }
        return self;
    }
    
    - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)oldBounds
    {
        return YES;
    }
    
    - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
    {
        NSArray * array = [[NSArray alloc]initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];
        CGRect visiableRect;
        visiableRect.origin = self.collectionView.contentOffset;
        visiableRect.size = self.collectionView.bounds.size;
    
        for (UICollectionViewLayoutAttributes * attributes in array)
        {
            if (CGRectIntersectsRect(attributes.frame, rect))
            {
                CGFloat distance = CGRectGetMidX(visiableRect) - attributes.center.x;
                distance = ABS(distance);
                if (distance < SCREEN_WIDTH/2 + self.itemSize.width)
                {
                    CGFloat zoom = 1 + ITEM_ZOOM * (1 - distance/THE_ACTIVE_DISTANCE);
                    attributes.transform3D = CATransform3DMakeScale(zoom, zoom, 1.0f);
                    attributes.transform3D = CATransform3DTranslate(attributes.transform3D, 0, -zoom * 25, 0);
                    attributes.alpha = zoom - ITEM_ZOOM;
                }
            }
        }
        return array;
    }
    
    - (CGPoint )targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
    {
        CGFloat offsetAdjustment = MAXFLOAT;
        CGFloat horizontalCenter_X = proposedContentOffset.x + CGRectGetWidth(self.collectionView.bounds)/2.0;
        CGRect targetRect = CGRectMake(proposedContentOffset.x, 0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
        NSArray *array = [super layoutAttributesForElementsInRect:targetRect];
        for (UICollectionViewLayoutAttributes * attributes in array)
        {
            CGFloat itemHorizontalCenter_X = attributes.center.x;
            if (ABS(itemHorizontalCenter_X - horizontalCenter_X) < ABS(offsetAdjustment))
            {
                offsetAdjustment = itemHorizontalCenter_X - horizontalCenter_X;
            }
        }
        return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
    }
    
    第二步,创建collectionView
    #import "ZLCollectionView.h"
    #import "ZLFlowLayout.h"
    
    static NSString * identifier = @"collecitonView_cell";
    @interface ZLCollectionView ()<UICollectionViewDelegate,UICollectionViewDataSource>
    
    @property (nonatomic,strong) UICollectionView *mainCollectionView;
    @property (nonatomic,assign) NSInteger itemCount;
    @property (nonatomic,assign) CGRect collectionViewFrame;
    
    @end
    
    @implementation ZLCollectionView
    
    
    + (instancetype)collectionViewWithFrame:(CGRect)frame itemCount:(NSInteger)itemCount
    {
        return [[self alloc]initWithFrame:frame itemCount:itemCount];
    }
    
    - (instancetype)initWithFrame:(CGRect)frame itemCount:(NSInteger)itemCount
    {
        self = [super initWithFrame:frame];
        if (self)
        {
            self.itemCount = itemCount;
            self.collectionViewFrame = frame;
            [self createCollectionViewStyle];
        }
        return self;
    }
    
    
    - (void)createCollectionViewStyle
    {
        ZLFlowLayout *layout = [[ZLFlowLayout alloc]init];
        self.mainCollectionView = [[UICollectionView alloc]initWithFrame:self.collectionViewFrame collectionViewLayout:layout];
        self.mainCollectionView.delegate = self;
        self.mainCollectionView.dataSource = self;
        self.mainCollectionView.backgroundColor = [UIColor groupTableViewBackgroundColor];
        self.mainCollectionView.showsHorizontalScrollIndicator = NO;
        [self addSubview:self.mainCollectionView];
    
        NSLog(@"%f -- %f",layout.itemSize.width,layout.itemSize.height); //375.404
    
    
        //注册cell
        [self.mainCollectionView registerClass:[UICollectionViewCell class]
                    forCellWithReuseIdentifier:identifier];
    }
    
    - (NSInteger )numberOfItemsInSection:(NSInteger)section
    {
        return 1;
    }
    - (NSInteger )collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
    {
        return self.itemCount;
    }
    - (__kindof CustomCollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        CustomCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier
                                                                               forIndexPath:indexPath];
    
        /*这段代码的作用就是:
         *当直接往细胞上面添加视图内容时,随着滑动,可能会出现内容重叠的问题。
         *但是在自定义细胞时使用这段代码,就会移除细胞的所有子视图,
         *使用Masonry给电池子视图上的内容进行约束就会崩溃,或者直接给细胞上的内容进行约束时就会出现细胞显示而上面内容不显示的问题。
        //这一步,防止cell上面的内容发生重叠
        for (UIView * view in cell.subviews)
        {
            [view removeFromSuperview];
        }
         */
    
        cell.backgroundColor = [UIColor lightGrayColor];
        cell.layer.masksToBounds = YES;
        cell.layer.cornerRadius = 5.0f;
    
        
        NSLog(@"Cell:%f--%f",cell.frame.size.width,cell.frame.size.height);
        return cell;
    }
    
    - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
    {
        self.selectedItems(indexPath);
    }
    
    - (void)didSelectedItemsWithBlock:(DidSelectedItems)selectedItems
    {
        self.selectedItems = selectedItems;
    }
    @end
    
    第三步、使用
    ZLCollectionView *collectionView = [ZLCollectionView collectionViewWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height) itemCount:30];
    [collectionView didSelectedItemsWithBlock:^(NSIndexPath *indexPath) {
       NSLog(@"ItemTag:%ld",indexPath.item);
    }];
    [self.view addSubview:collectionView];
    

    说明

    可以通过修改ZLFlowLayoutConstants.hITEM_ZOOMTHE_ACTIVE_DISTANCELEFT_OFFSET的值,来修改卡片item的大小样式

    之前会一直遇到这个警告提示

    2017-08-17 16:18:17.133324+0800 ZLCollectionViewFlowLayout[4342:480201] Logging only once for UICollectionViewFlowLayout cache mismatched frame
    2017-08-17 16:18:17.133680+0800 ZLCollectionViewFlowLayout[4342:480201] UICollectionViewFlowLayout has cached frame mismatch for index path <NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0} - cached value: {{55, 86.346884732472063}, {210, 339.80584123617336}}; expected value: {{60, 122}, {200, 323.62461070111749}}
    2017-08-17 16:18:17.133859+0800 ZLCollectionViewFlowLayout[4342:480201] This is likely occurring because the flow layout subclass ZLFlowLayout is modifying attributes returned by UICollectionViewFlowLayout without copying them
    

    后来百度了一下,将layoutAttributesForElementsInRect中的NSArray * array = [super layoutAttributesForElementsInRect:rect];换成NSArray * array = [[NSArray alloc]initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];就解决了。

    这是我的github地址,有喜欢的可以star一下,谢谢了。

    =======================================================================================================

    更正

    2017-11-24
    =======================================================================================================

    之前有小伙伴私信问起使用Masonry自定义cell,会出现-崩溃-或者-内容显示不出来但cell能显示-的问题,把ZLCollectionView.m中的下面一段代码注释了就行。

    //这一步,防止cell上面的内容发生重叠
        for (UIView * view in cell.subviews)
        {
            [view removeFromSuperview];
        }
    

    这段代码的作用就是:

    当直接往cell上面添加视图内容时,随着滑动,可能会出现内容重叠的问题。但是在自定义cell时使用这段代码,就会移除cell的所有子视图,使用Masonry给cell子视图上的内容进行约束就会崩溃,或者直接给cell上的内容进行约束时就会出现cell显示而上面内容不显示的问题。

    我修改一下demo,标注了这个问题,添加了自定义cell的内容,有需要的小伙伴可以去我github下载一下。

    相关文章

      网友评论

        本文标题:(修正)分享一个卡片式布局的collectionView

        本文链接:https://www.haomeiwen.com/subject/vacnrxtx.html