美文网首页iOS学习专题iOS新手学习
iOS使用UICollectionView实现一个类似于微信朋友

iOS使用UICollectionView实现一个类似于微信朋友

作者: Coder_张三 | 来源:发表于2019-07-13 18:02 被阅读25次

    前言

    最近在做项目的时候遇到客户需要实现类似于微信朋友圈中全文&收起的功能,虽然做出来之后觉得很简单,但是可是把我折腾了好多天,换了两种方式去写,没能实现的方式我这里就不介绍了。好了,下面开始说说我自己的实现思路,思路是很重要的!下面先给两张效果图,动态图就暂时不展示了,嫌麻烦!
    未展开的时候:

    效果图1.png

    展开的时候:


    效果图2.png

    实现思路:

    一、首先,我这里使用UICollectionView作为列表视图来实现,滚动方向设置为垂直滚动,因为要保证项目代码的统一性,就不要UITableView了来实现了,UICollectionView的一些基础设置我这里就不多介绍了,网上有很多关于UICollectionView的介绍的。
    二、每一条数据都使用一个UICollectionViewCell来展示。
    三、往UICollectionViewCell里添加相应的子视图,看一下分析图。


    分析图.png

    头像使用UIImageView,标题和文本内容使用UILabel,控制展开还是收起的使用UIButton,图片的话我使用一个UICollectionView作为多张图片的容器视图,其实图片大家也看到了,是一个九宫格,底部的分割线添加一个View到Cell的最底部就好了。
    四、有了上面的一个基本思路之后,我是这样实现的:先使用懒加载创建一个UICollectionView添加到父类视图,并注册UICollectionViewCell,记得要遵守相关的协议,这里先展示部分代码。

    @interface DTCircleViewController ()<UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
    @property(nonatomic, strong) UICollectionView *collectionView;
    @property(nonatomic, strong) NSMutableArray *circleArr;     //朋友圈列表模型数组
    /**存储是否展开的BOOL值,默认折叠*/
    @property (nonatomic, strong) NSMutableArray * boolArr;
    @end
    
    @implementation DTBaseViewController
    static NSString * const DTCollectionCircleCellID = @"DTCollectionCircleCellID";
    
    #pragma mark 懒加载
    - (NSMutableArray *)boolArr {
        if (!_boolArr) {
            _boolArr = [[NSMutableArray alloc] init];
        }
        return _boolArr;
    }
    
    - (NSMutableArray *) circleArr {
        if (!_ circleArr) {
            _ circleArr = [NSMutableArray array];
        }
        return _ circleArr;
    }
    - (UICollectionView *)collectionView {
        if (!_collectionView) {
            UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
            layout.minimumLineSpacing = 0;
            layout.minimumInteritemSpacing = 0;
            layout.scrollDirection = UICollectionViewScrollDirectionVertical;
            
            _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
            _collectionView.backgroundColor = [UIColor blackColor];//DTRGBColor(233, 233, 233);
            _collectionView.showsVerticalScrollIndicator = NO;
            _collectionView.bounces = YES;
            _collectionView.dataSource = self;
            _collectionView.delegate = self;
            _collectionView.scrollsToTop = NO;
            _collectionView.scrollEnabled = YES;
            _collectionView.alwaysBounceVertical = YES;
    // 2、注册cell
        [self.collectionView registerClass:[DTCollectionCircleCell class] forCellWithReuseIdentifier:DTCollectionCircleCellID];
        }
        
        return _collectionView;
    }
    
    #pragma mark 系统回调
    - (void)viewDidLoad {
        [super viewDidLoad];
        //添加子视图
        [self addSubviews];
        
        //加载数据
        [self loadData];
    }
    
    - (void)addSubviews {
        //1、添加collectionView
        self.collectionView.frame = self.view.bounds;
        [self.view addSubview:self.collectionView];
        
    }
    

    既然我们遵守协议设置了代理就必须实现它的代理方法。

    #pragma mark UICollectionViewDelegate & UICollectionViewDataSource
    - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
        return self. circleArr.count;
    }
    
    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
        DTCollectionCircleCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:DTCollectionCircleCellID forIndexPath:indexPath];
        
        DTCircle *circle = self. circleArr[indexPath.item];
    //使用三目运算
        DTCircleFrame *circleFrame = [self.boolArr[indexPath.item] boolValue] ? [[DTCircleFrame alloc] initWithCircle:circle isFold:NO] : [[DTCircleFrame alloc] initWithCircle:circle isFold:YES];
        cell.circleFrame = circleFrame;
        
        MJWeakSelf
        [cell setBlockButton:^(UIButton * _Nonnull button) {
            
            //判断改变bool值
            if ([weakSelf.boolArr[indexPath.item] boolValue] == YES) {
                [weakSelf.boolArr replaceObjectAtIndex:indexPath.item withObject:@NO];
            }else {
                [weakSelf.boolArr replaceObjectAtIndex:indexPath.item withObject:@YES];
            }
            
            [UIView performWithoutAnimation:^{
                [weakSelf.collectionView reloadItemsAtIndexPaths:@[indexPath]];
            }];
            
        }];
        return cell;
    }
    
    #pragma mark UICollectionViewDelegateFlowLayout
    - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
        if (self.boolArr.count == 0) {
            return CGSizeMake(DTScreenW, 0);
        }
        
        DTCircle *circle = self. circleArr[indexPath.item];
    //使用三目运算
        DTCircleFrame *circleFrame = [self.boolArr[indexPath.item] boolValue] ? [[DTCircleFrame alloc] initWithCircle:circle isFold:NO] : [[DTCircleFrame alloc] initWithCircle:circle isFold:YES];
        return CGSizeMake(DTScreenW, floor(circleFrame.cellHeight));
    }
    

    好了,以上控制器的代码除了发送网络请求获取数据的代码没有粘出来之外,其他的基本都在这儿了。仔细看我的代码会发现我使用boolArr这个数组来判断相关的功能,这个等会儿再说,我们先说说获取数据之后我是如何布局的。
    你们会发现我的代码有两个模型,分别是DTCircle和DTCircleFrame,顾名思义,一个是没有尺寸的模型,一个是带有尺寸的模型。很明显,带有尺寸的模型是专门用来计算Cell里面子控件的尺寸的。
    为什么不在cell里计算子控件的尺寸呢?别看效果图上没有多少个子控件,其实计算尺寸的代码一路下来也得好多行,更何况很有可能以后还会添加其他的子控件,为了代码的简洁性和充分使用面向对象的机制。所以我就把计算尺寸的代码单独放到DTCircleFrame里面,这样方便我们管理代码。
    下面贴出DTCircleFrame的相关代码。

    .h文件

    #import <Foundation/Foundation.h>
    #define nicknameFont [UIFont systemFontOfSize:15]
    #define textFont [UIFont systemFontOfSize:14]
    
    NS_ASSUME_NONNULL_BEGIN
    @class DTCircle;
    
    /**带尺寸的宝圈模型*/
    @interface DTCircleFrame : NSObject
    /**是否是折叠状态*/
    @property(nonatomic, assign) BOOL isFold;
    /**不带尺寸的模型*/
    @property(nonatomic, strong) DTCircle *circle;
    /**底部的线的frame*/
    @property(nonatomic, assign) CGRect bottomLineFrame;
    /**用户头像的frame*/
    @property(nonatomic, assign) CGRect avatarFrame;
    /**用户昵称的frame*/
    @property(nonatomic, assign) CGRect nicknameFrame;
    /**内容文本的frame*/
    @property(nonatomic, assign) CGRect contentTextFrame;
    /**文本label的行数*/
    @property(nonatomic, assign) NSUInteger textLabelRow;
    /**展开&收起按钮的frame*/
    @property(nonatomic, assign) CGRect stateBtnFrame;
    /**图片容器的frame*/
    @property(nonatomic, assign) CGRect collectionViewFrame;
    /**图片的宽高*/
    @property(nonatomic, assign) CGFloat pictureWH;
    /**cell的高度*/
    @property(nonatomic, assign) CGFloat cellHeight;
    
    /**自定义计算尺寸的实例方法*/
    - (instancetype)initWithCircle:(DTCircle *)circle isFold:(BOOL)isFold;
    @end
    

    .m文件

    #import "DTCircleFrame.h"
    #import "DTCircle.h"
    #define DTCircleMargin 10
    
    @implementation DTCircleFrame
    
    - (instancetype)initWithCircle:(DTCircle *)circle isFold:(BOOL)isFold {
        if (!circle) return nil;
        self = [super init];
        
        self.circle = circle;
        self.isFold = isFold;
        
        // 1、用户头像的frame
        self.avatarFrame = CGRectMake(DTCircleMargin, DTCircleMargin, 40, 40);
        
        // 2、用户昵称的frame
        CGFloat nicknameX = CGRectGetMaxX(self.avatarFrame) + DTCircleMargin;
        CGSize nicknameSize = [self sizeWithText:circle.name font:nicknameFont maxWidth:DTScreenW - nicknameX - DTCircleMargin];
        self.nicknameFrame = CGRectMake(nicknameX, DTCircleMargin, nicknameSize.width, nicknameSize.height);
        
        // 3、内容文本的frame
        CGFloat textX = CGRectGetMaxX(self.avatarFrame) + DTCircleMargin;
        CGFloat textY = CGRectGetMaxY(self.avatarFrame) - 15;
        CGSize textSize = [self sizeWithText:circle.brief font:textFont maxWidth:DTScreenW - textX - DTCircleMargin];
        //每行文字的高度:
        CGFloat lineHeight = textFont.lineHeight;
        //label的总行数
        self.textLabelRow = textSize.height / lineHeight;
        CGFloat textH = 0;
        //判断在初始化显示的时候label是否超过两行,如果超过,显示状态按钮(展开&收起)
        if (self.textLabelRow > 2) {
            if (isFold == NO) {
                textH = textSize.height;
            }else {
                textH = lineHeight * 2;
            }
            
        }else {
            textH = textSize.height;
        }
        
        self.contentTextFrame = CGRectMake(textX, textY, textSize.width, textH);
        
        // 4、展开&收起按钮的frame
        CGFloat stateX = CGRectGetMaxX(self.avatarFrame) + DTCircleMargin;
        CGFloat stateY = CGRectGetMaxY(self.contentTextFrame) + DTCircleMargin;
        self.stateBtnFrame = CGRectMake(stateX, stateY, 60, 20);
        
        // 5、图片容器的frame
        CGFloat pictureViewX = CGRectGetMaxX(self.avatarFrame) + DTCircleMargin;
        CGFloat pictureViewY = CGRectGetMaxY(self.stateBtnFrame) + DTCircleMargin;
        CGFloat pictureViewW = DTScreenW - pictureViewX - DTCircleMargin;
        NSUInteger count = circle.pictures.count;
        NSUInteger rows = count / 3;
        if (count <= 3) {
            rows = count == 0 ? 0 : 1;
        }else {
            rows = count % 3 != 0 ? rows + 1 : rows;
        }
        self.pictureWH = (pictureViewW / 3);
        self.collectionViewFrame = CGRectMake(pictureViewX, pictureViewY, pictureViewW, self.pictureWH * rows);
        
        // 6、底部的线的frame
        self.bottomLineFrame = CGRectMake(0, CGRectGetMaxY(self.collectionViewFrame) + DTCircleMargin, DTScreenW, 1);
        
        // 7、cell的高度
        self.cellHeight = CGRectGetMaxY(self.bottomLineFrame);
        
        return self;
    }
    
    /**
     *  获得字符串的 size
     *
     *  @param text 字符串
     *  @param font 字体样式
     *
     *  @return size
     */
    -(CGSize)sizeWithText:(NSString *)text font:(UIFont *)font {
        CGSize maxSize = CGSizeMake(MAXFLOAT, MAXFLOAT);
        /*计算获取字符串的宽和高*/
        NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
        attrs[NSFontAttributeName] = font;
        CGSize textSize = [text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;
        
        return textSize;
    }
    
    /**
     *  获得字符串的 size+最大宽度
     *
     *  @param text 字符串
     *  @param font 字体样式
     *  @param maxWidth 最大宽度
     *
     *  @return size
     */
    -(CGSize)sizeWithText:(NSString *)text font:(UIFont *)font maxWidth:(CGFloat)maxWidth {
        CGSize maxSize = CGSizeMake(maxWidth, MAXFLOAT);
        /*计算获取字符串的宽和高*/
        NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
        attrs[NSFontAttributeName] = font;
        CGSize textSize = [text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;
        
        return textSize;
    }
    

    我就说一下内容文本和图片九宫格的计算方式,因为其他的都是一些很基本的布局,先说一下内容文本的计算方式吧。
    想象一下,内容文本是不是可能会多行,可能会单行。那超过两行的就只让他显示两行,并且超过的部分使用...表示,超出的话显示展开按钮,否则隐藏。
    好了,根据分析,我们有几点需要做:
    1、计算这个内容文本(UILabel)一共有多少行。
    2、判断是否超过两行,超过两行只显示两行,并且多余的部分使用...表示,显示展开按钮
    3、如果没有超过两行,就按实际的行数显示,并隐藏展开按钮。
    以上就是我们需要做的3件事,怎么计算内容文本一共有多少行呢?
    公式就是:总行数 = 内容文本的总高度 / 每行的高度;
    下面贴上代码:
    这是计算内容文本的总高度

    /**
     *  获得字符串的 size+最大宽度
     *
     *  @param text 字符串
     *  @param font 字体样式
     *  @param maxWidth 最大宽度
     *
     *  @return size
     */
    -(CGSize)sizeWithText:(NSString *)text font:(UIFont *)font maxWidth:(CGFloat)maxWidth {
        CGSize maxSize = CGSizeMake(maxWidth, MAXFLOAT);
        /*计算获取字符串的宽和高*/
        NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
        attrs[NSFontAttributeName] = font;
        CGSize textSize = [text boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;
        
        return textSize;
    }
    

    这样使用:

    // 3、内容文本的frame
    CGFloat textX = CGRectGetMaxX(self.avatarFrame) + DTCircleMargin;
        CGFloat textY = CGRectGetMaxY(self.avatarFrame) - 15;
    CGSize textSize = [self sizeWithText:circle.brief font:textFont maxWidth:DTScreenW - textX - DTCircleMargin];
    
    //获取每行文字的高度()这里的字体大小一定要和你所展示的字体大小一致,否则计算不准确
        CGFloat lineHeight = textFont.lineHeight;
    
        //记录label的总行数
        self.textLabelRow = textSize.height / lineHeight;
    
    CGFloat textH = 0;
        //判断在初始化显示的时候label是否超过两行,如果超过,显示状态按钮(展开&收起)
        if (self.textLabelRow > 2) {
            if (isFold == NO) {
                textH = textSize.height;
            }else {
                textH = lineHeight * 2;
            }
            
        }else {
            textH = textSize.height;
        }
    
    self.contentTextFrame = CGRectMake(textX, textY, textSize.width, textH);
    

    可以看到,先判断了行数,再判断实例化时是否是折叠状态,再去确定它的高度,当我们控制住UILabel的高度时,就能达到我们想要的效果了!这里提一下,我使用了textLabelRow 来记录内容文本的行数,用于第二和第三件事的判断,这里贴上Cell的相关代码:

    // 3、设置内容文本的
        self.contentText.frame = circleFrame.contentTextFrame;
        self.contentText.text = circle.brief;
        self.contentText.numberOfLines = circleFrame.textLabelRow;
        
        // 4、设置展开&收起按钮
        self.stateBtn.frame = circleFrame.stateBtnFrame;
        self.stateBtn.hidden = circleFrame.textLabelRow <= 2 ? YES : NO;
        circleFrame.isFold ? [self.stateBtn setTitle:@"展开" forState:UIControlStateNormal] : [self.stateBtn setTitle:@"收起" forState:UIControlStateNormal];
    

    到这里内容文本多行和单行以及按钮的显示逻辑就完成了!下面开始分析九宫格的算法。
    提到九宫格我这里建议使用UICollectionView去实现,充分利用他的循环利用机制,而且代码相对简单。下面贴出相关代码:

    #import "DTCollectionCircleCell.h"
    #import "DTCollectionCirclePictureCell.h"
    
    #import "DTCircle.h"
    #import "DTCircleFrame.h"
    
    @interface DTCollectionCircleCell () <UICollectionViewDelegate, UICollectionViewDataSource>
    /**图片容器*/
    @property(nonatomic, strong) UICollectionView *collectionView;
    @end
    static NSString * const DTCollectionCirclePictureCellID = @"DTCollectionCirclePictureCellID";
    
    #pragma mark 懒加载
    - (UICollectionView *)collectionView {
        if (!_collectionView) {
            UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
            layout.minimumLineSpacing = 0;
            layout.minimumInteritemSpacing = 0;
            layout.scrollDirection = UICollectionViewScrollDirectionVertical;
            
            _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
            _collectionView.backgroundColor = [UIColor whiteColor];
            _collectionView.showsVerticalScrollIndicator = NO;
            _collectionView.bounces = YES;
            _collectionView.dataSource = self;
            _collectionView.delegate = self;
            _collectionView.scrollsToTop = NO;
            _collectionView.scrollEnabled = NO;
            _collectionView.alwaysBounceVertical = YES;
            
            //注册cell
            [_collectionView registerClass:[DTCollectionCirclePictureCell class] forCellWithReuseIdentifier:DTCollectionCirclePictureCellID];
        }
        
        return _collectionView;
    }
    
    //这里是我自己写的一个基类,当需要添加子视图的时候调用父类的方法就好了,就没必要写一堆麻烦的代码。
    - (void)addSubviews {
        [super addSubviews];
        // 1、添加子视图
        [self.contentView addSubview:self.collectionView];
    }
    
    #pragma mark UICollectionViewDelegate & UICollectionViewDataSource
    - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
        DTCircle *circle = self.circleFrame.circle;
        return circle.pictures.count;
    }
    
    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
        //图片Cell
        DTCollectionCirclePictureCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:DTCollectionCirclePictureCellID forIndexPath:indexPath];
        DTCircle *circle = self.circleFrame.circle;
        cell.picfilepath = circle.pictures[indexPath.item];
        
        return cell;
    }
    

    可以看到,把UIImageView放进DTCollectionCirclePictureCell里,然后传一个图片地址过去就可以了。还有一点,我这里并没有实现UICollectionViewDelegateFlowLayout的代理方法(因为比较麻烦,就使用了另一种方法)。
    接下来开始计算如何布局九宫格:
    1、计算DTCollectionCirclePictureCell的宽度
    2、计算有多少列
    3、计算有多少行
    我为什么会先计算DTCollectionCirclePictureCell的宽度呢?因为你只要计算好了每个Cell的宽度,在使用UICollectionView时候它会帮你自动适配,比如我设置DTCollectionCirclePictureCell的宽度为UICollectionView的3分之一,假如你有两个以上的图片它就会自动帮你往水平方向排,如果超过三个,它会自动帮你换行,以此类推,这个主要得益于UICollectionViewFlowLayout的布局。
    所以,只要计算好DTCollectionCirclePictureCell的宽度,那么列数就计算好了!
    下面计算行数,还是先贴上相关代码吧:

    NSUInteger count = circle.pictures.count;
        NSUInteger rows = count / 3;
        if (count <= 3) {
            rows = count == 0 ? 0 : 1;
        }else {
            rows = count % 3 != 0 ? rows + 1 : rows;
        }
    
    // DTCollectionCirclePictureCell的宽和高
        self.pictureWH = (pictureViewW / 3);
    

    以上就是行数的计算,其实就是图片的个数除以3,为什么除3呢,因为一行有3张图片。当图片个数小于3的时候, count / 3这个会自动帮你取整,最后会等于0,所以这个时候得给它+1。
    接下来还有一种情况,比如当图片有7张的时候,算一下7/3其实是的2,因为NSUInteger会帮我们取整。但是当有7张图片的时候是只有两行吗?很明显,不是。
    所以我们可以判断他的余数,当他的余数等于0的时候,说明行数是正确的,当余数不等0的时候,我们就给它+1,这样就解决啦!
    那我们在Cell里怎么使用呢?下面贴出相关代码:

    // 5、设置图片容器的
        self.collectionView.frame = circleFrame.collectionViewFrame;
        //取出layout
        UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
        layout.itemSize = CGSizeMake(circleFrame.pictureWH, circleFrame.pictureWH);
        [self.collectionView reloadData];
    
        可以看到,确定了整个容器的frame,在把设置每个cell的宽和高,取出layout,再去设置cell的size也是一种办法哦。
        还剩最后一个了,那就是控制展开和折叠。说实在的,这个可是把我折磨的不清,老规矩,还是先讲思路:
        1、声明一个可变数组属性,并懒加载它,用于存储是否展开&折叠的BOOL值,默认折叠
        2、将所有的cell设置为默认折叠状态
        3、当点击按钮的时候让它显示全文,并且展开文字变成收起文字
        4、再次点击的时候收回来,显示折叠状态
    

    小面是具体实现,贴上代码:

    NSMutableArray *tmGoodsArr = [DTCircle mj_objectArrayWithKeyValuesArray:responseObj[@"result"]];
     if (weakSelf.page == 1) {   //下拉
        self.circleFrameArr = tmGoodsArr;
        for (int i = 0; i < self.circleFrameArr.count; i++) {
            //所有的cell默认折叠状态
            [self.boolArr addObject:@NO];
        }
       }else {         //上拉
            NSMutableArray *tmpBoolArr = [NSMutableArray array];
             for (int i = 0; i < tmGoodsArr.count; i++) {
              //后面添加的cell默认折叠状态
              [tmpBoolArr addObject:@NO];
              }
            //将后面添加的cell默认折叠状态添加到数组末尾
            [self.boolArr addObjectsFromArray:tmpBoolArr];
            //将上拉的数据添加到数组末尾
            [weakSelf.circleArr addObjectsFromArray:tmGoodsArr];
      }
    
    [weakSelf.collectionView reloadData];
    

    这段代码,第一第二点就都实现了, 使用for循环就是为了把所有的cell都默认设置为折叠状态,往数组里添加@NO。
    所以在数据源方法里我可以这样写:

    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
        DTCollectionCircleCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:DTCollectionCircleCellID forIndexPath:indexPath];
        
        DTCircle *circle = self.circleFrameArr[indexPath.item];
     //使用三目运算
        DTCircleFrame *circleFrame = [self.boolArr[indexPath.item] boolValue] ? [[DTCircleFrame alloc] initWithCircle:circle isFold:NO] : [[DTCircleFrame alloc] initWithCircle:circle isFold:YES];
        cell.circleFrame = circleFrame;
        
        MJWeakSelf
        [cell setBlockButton:^(UIButton * _Nonnull button) {
            
            //判断改变bool值
            if ([weakSelf.boolArr[indexPath.item] boolValue] == YES) {
                [weakSelf.boolArr replaceObjectAtIndex:indexPath.item withObject:@NO];
            }else {
                [weakSelf.boolArr replaceObjectAtIndex:indexPath.item withObject:@YES];
            }
            
            [UIView performWithoutAnimation:^{
                [weakSelf.collectionView reloadItemsAtIndexPaths:@[indexPath]];
            }];
            
        }];
        return cell;
    }
    

    可以看到,我先取出数组中BOOL的值,[self.boolArr[indexPath.item] boolValue] ,如果取出的BOOL值是YES,那么就是折叠状态 : [[DTCircleFrame alloc] initWithCircle:circle isFold:YES]; 如果是NO,那就是展开状态:[[DTCircleFrame alloc] initWithCircle:circle isFold:NO],我这里一样使用三目运算。
    下面block是当点击按钮时传过来的响应事件,首先判断改变当前点击的Cell的状态,改变bool值。再去刷新当前cell的数据,因为reloadItemsAtIndexPaths方法默认带有动画,但我们不需要,所以使用performWithoutAnimation方法去掉。
    你可以这样就完?其实还有最后一点,那就是改变当前Cell的高度,既然展开了,那UILabel就变高了,所以Cell的高度也要相应的改变。下面贴代码:

    #pragma mark UICollectionViewDelegateFlowLayout
    - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
        if (self.boolArr.count == 0) {
            return CGSizeMake(DTScreenW, 0);
        }
        
        DTCircle *circle = self.circleFrameArr[indexPath.item];
        DTCircleFrame *circleFrame = [self.boolArr[indexPath.item] boolValue] ? [[DTCircleFrame alloc] initWithCircle:circle isFold:NO] : [[DTCircleFrame alloc] initWithCircle:circle isFold:YES];
        return CGSizeMake(DTScreenW, floor(circleFrame.cellHeight));
    }
    

    其实就是实现了UICollectionViewDelegateFlowLayout的代理方法,设置每个Cell的Size,这段代码其实跟上面的一样,也是使用三目运算,先取出数组中BOOL的值,[self.boolArr[indexPath.item] boolValue] ,如果取出的BOOL值是YES,那么就是折叠状态 : [[DTCircleFrame alloc] initWithCircle:circle isFold:YES]; 如果是NO,那就是展开状态,从而改变设置Cell的高度。
    这个功能到这里基本上就已经完成啦!其实难度是在点击展开刷新,改变内容文本的高度,改变cell的高度,这个我也是一个一个方法试过来的,折腾了好久,其实仔细一想,我之前有做过类似QQ好友列表的展开&收起功能,就是我的上一篇文章,然后就是使用了同一种方法,才完美解决刷新改变高度的问题。
    源码就不放出来了,因为没有网络数据测试,我也懒得自己写,咱们主要知道思路就好啦!这个也是我的笔记,最后如果有什么不懂的欢迎提问!我会在第一时间解答,如果还有什么需要待优化的地方,还希望各位大神指点!谢谢啦!

    相关文章

      网友评论

        本文标题:iOS使用UICollectionView实现一个类似于微信朋友

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