美文网首页在iOS开发的道路上越走越远
解决CollectionView复用机制带来的麻烦(非原创)

解决CollectionView复用机制带来的麻烦(非原创)

作者: CarrieQ | 来源:发表于2016-12-19 14:14 被阅读1521次

    需求:

    需要的Button 的个数不一定 Button上显示的字符串的长度不一定,Button上显示的字符串和Button的个数全部从服务器下发 ,当Button的个数在屏幕上展示不下的时候要可以左右滑动 以展示更多的Button

    方案:

    综合以上因素 我选择利用CollectionView实现此功能.

    其中遇到几个问题:

    首先说明: cell中有一个Label 属性

    
    - (UILabel *)textLabel{
    
        if (!_textLabel) {
    
             _textLabel = [[UILabel alloc]initWithFrame:self.contentView.frame];
    
            _textLabel.layer.borderWidth = 1;
    
            _textLabel.layer.borderColor = [UIColor lightGrayColor].CGColor;
    
            _textLabel.font = [UIFont systemFontOfSize:13];
     
            _textLabel.textAlignment = NSTextAlignmentCenter;
    
            _textLabel.textColor = [UIColor blackColor];
    
            [self.contentView addSubview:_textLabel];
    
        }
    
        _textLabel.frame = CGRectMake(CGRectGetMinX(self.contentView.frame), CGRectGetMinY(self.contentView.frame), self.frame.size.width,     self.frame.size.height);
    
        return _textLabel;
    
    }
    
    

    问题 一 :因为选中cell时 cell的字和边框要变成红色 ,当再点击其他Item时此item要变为原来的样子

    这个功能主要在collectionView的两个代理方法中实现

    - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
    
    - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath;;
    

    具体实现如下:

    /**
    
    *  当点击item时会调用此方法 在此方法中把点击的item的textLabel属性的字体颜色和边框改变颜色
    
    *
    
    */
    
    - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    
    JWCCollectionViewCell *cell = (JWCCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
    
    cell.textLabel.textColor = [UIColor redColor];
    
    cell.textLabel.layer.borderColor = [UIColor redColor].CGColor;
    
    }
    
    /**
    
    *  当点击其他cell时调用此方法 比如点击第一个cell的时候调用上面的方法,当点击第二个的时候先调用此方法,然后再调用上面的方法
    
    在此方法中获取第一次点击的cell 即
    
    JWCCollectionViewCell *cell = (JWCCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
    
    把此cell 的textLabel属性变回原来的样子 然后系统会调用上面的代理方法把点击的第二个cell的textLabel属性的字体颜色和边框改变颜色
    
    这样就实现了 点击此cell改变颜色 再点击其他的cell是时 上一个点击的cell恢复到原来的样子
    
    *
    
    */
    
    - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath{
    
    JWCCollectionViewCell *cell = (JWCCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
    
    cell.textLabel.textColor = [UIColor blackColor];
    
    cell.textLabel.layer.borderColor = [UIColor lightGrayColor].CGColor;
    
    }
    

    问题二:

    假如cell的个数太多的话肯定会发生复用的问题:

    比如数:屏幕的宽度只能显示3个cell 但是现在有四个需要显示,那么当点击第一个cell 向左滑动时会显示第四个 这个时候第四个item就是从复用队列中取出一个cell ,那么这个cell可能还保留这上一个的属性 比如说字体是红色的 其宽度可能会很大,不适合当前的string的宽度 ,左右滑动的时候cell 之间的间隔也会发生错乱 这绝不是想要的结果 。出现这个原因主要是cell的复用产生的。

    解决方法:

    在cell复用之前把cell恢复到初始化状态,那么这就要重写 - (void)prepareForReuse方法

    具体做法如下:

    
    - (void)prepareForReuse{
    
    [super prepareForReuse];
    
    _textLabel.frame = self.contentView.frame;
    
    _textLabel.layer.borderWidth = 1;
    
    _textLabel.textColor = [UIColor blackColor];
    
    _textLabel.layer.borderColor = [UIColor lightGrayColor].CGColor;
    
    }
    

    这样就解决了颜色问题

    注意:prepareForReuse这个方法是CollectionViewCell的方法

    但是错乱问题仍然没有解决:这主要是因为在定义_textLabel 时它的frame设置问题 应该在添加一句

    _textLabel.frame = CGRectMake(CGRectGetMinX(self.contentView.frame), CGRectGetMinY(self.contentView.frame), self.frame.size.width, self.frame.size.height);
    

    具体是这样的


    - (UILabel *)textLabel{
    
    if (!_textLabel) {
    
    _textLabel = [[UILabel alloc]initWithFrame:self.contentView.frame];
    
    _textLabel.layer.borderWidth = 1;
    
    _textLabel.layer.borderColor = [UIColor lightGrayColor].CGColor;
    
    _textLabel.font = [UIFont systemFontOfSize:13];
    
    _textLabel.textAlignment = NSTextAlignmentCenter;
    
    _textLabel.textColor = [UIColor blackColor];
    
    [self.contentView addSubview:_textLabel];
    
    }
    
    _textLabel.frame = CGRectMake(CGRectGetMinX(self.contentView.frame), CGRectGetMinY(self.contentView.frame), self.frame.size.width, self.frame.size.height);
    
    return _textLabel;
    
    }
    

    问题三:

    比如说 当点击第一个cell 后 向左滑动让第一个消失在界面中,当再滑出界面时你会发现它的选中状态消失了,而你并没有选中其他的cell ,这是为什么???因为上一问题的解决方法导致了这一步。那么该怎么样解决才能不至于解决上面的问题引出下面的问题呢??方法如下:

    1, 定义一个全局变量:

    @property (nonatomic ,assign)NSInteger selectIndex;
    

    2 这个先给它赋一个永远也不可能达到的值

    self.selectIndex = MAXFLOAT;  //至于为什么赋这么大下面说
    

    3 在

     - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
    

    这个代理方法中把 indexPath.row 值赋值给 self.selectIndex如下

    self.selectIndex = indexPath.row;
    

    这就记住了所选择的item的位置

    4,在 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;这个方法中可能会从复用队列中去处cell 产生复用 但在复用之前会调用- (void)prepareForReuse方法 又会恢复原样使选中状态消失,这个时候要判断一下,判断这个方法中的 indexPath.row 是否等于 self.selectIndex 如果等于 使其变为选中的状态,具体代码如下:

    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    JWCCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cellID" forIndexPath:indexPath ];
    
    if (indexPath.row == _selectIndex ) {
    
    cell.textLabel.textColor = [UIColor redColor];
    
    cell.textLabel.layer.borderColor = [UIColor redColor].CGColor;
    
    }
    
    cell.textLabel.text = [self.dataSourceArr objectAtIndex:indexPath.item];
    
    NSLog(@"%@",cell.textLabel.text);
    
    return cell;
    
    }
    

    这样即使选中的cell 消失在界面中在出现的时候也不会改变选中状态。

    为什么要把self.selectIndex = MAXFLOAT

    如果不这样赋值 假如赋的值为0 那么每次打开时第一个cell就是选中状态,但是你并没有点击第一个。这主要是在cell生成的代理方法中的这一句代码引起的

    if (indexPath.row == _selectIndex ) {
    
    cell.textLabel.textColor = [UIColor redColor];
    
    cell.textLabel.layer.borderColor = [UIColor redColor].CGColor;
    
    }
    

    所以把self.selectIndex 赋值为 MAXFLOAT 这样就不会有当一启动APP的时候就有cell被选中的状态

    问题四:

    根据string的宽度不同来生成的cell的宽度也不同的功能是在

    - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
    

    实在这个方法中返回不通的CGSize 来改变每一个cell的宽度。

    在此可以计算字符串的宽度:

    具体的方法如下:

    #pragma mark - UICollectionViewFlowLayoutDelegate
    
    - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
    
    NSString *str =  [self.dataSourceArr objectAtIndex:indexPath.item];
    
    CGRect rect = [self getStringRect:str withFont:13];
    
    return CGSizeMake(rect.size.width, 30);
    
    }
    
    /**
    
    *  计算字符串的宽度的方法
    
    */
    
    - (CGRect)getStringRect:(NSString*)aString withFont :(CGFloat)font
    
    {
    
    CGRect rect;
    
    if(aString){
    
    CGRect rect = [aString boundingRectWithSize:CGSizeMake(MAXFLOAT, 30) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:font]} context:nil];
    
    return  rect;
    
    }
    
    return rect;
    
    }
    

    注意注意:!!!!

    • (CGRect)getStringRect:(NSString*)aString withFont :(CGFloat)font 在这个方法中需要传入字体的大小。

    这个大小一定要和 textLabel初始化时的字体大小一样 比如本例中在初始化的时候是13 那么在计算字符串的宽度的时候一定要是13 不然计算出的字符串的宽度比实际的要长 :会出现的问题是当左右滑动时会出现各个cell之间的间隔会出现改变 也许会两个cell会出现重叠!!

    _textLabel.font = [UIFont systemFontOfSize:13];
    

    原文地址:http://blog.csdn.net/godblessmyparents/article/details/50675263

    相关文章

      网友评论

        本文标题:解决CollectionView复用机制带来的麻烦(非原创)

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