Cell自適應(yīng)高度標(biāo)簽

前言

好久沒(méi)有更新自己的博客了,去年自己在博客總結(jié)說(shuō)到自己要堅(jiān)持更新博客,不管是否有技術(shù)含量,都要持之以恒,無(wú)論做什么,都貴在堅(jiān)持,我希望通過(guò)寫(xiě)博客能積累自己的技術(shù)與經(jīng)驗(yàn)。不多說(shuō)了,言歸正傳,下面開(kāi)始介紹我是如何實(shí)現(xiàn)Cell標(biāo)簽自適應(yīng)的吧。

一、本地?cái)?shù)據(jù)自適應(yīng)

  • 在做項(xiàng)目意見(jiàn)反饋的時(shí)候,需要選擇反饋類(lèi)型,整個(gè)界面是UITableView,我現(xiàn)在喜歡用自動(dòng)布局,用的Masonry布局框架,開(kāi)始選擇類(lèi)型是放在本地的,用Masonry實(shí)現(xiàn)cell高度自適應(yīng)還算相對(duì)簡(jiǎn)單的,下面是實(shí)現(xiàn)數(shù)據(jù)在本地高度自適應(yīng)的核心代碼,該方法在cell初始化方法中調(diào)用:
- (void)initSubviews {
    /** << init subviews > */
    CGFloat margin = 15.f;
    CGFloat spacing = 10.f;
    CGFloat maxWidth = ScreenWidth;
    __block CGFloat rowWidth = 0;
    __block BOOL isNeedChangeLine = YES;
    __block UIButton *lastButton = nil;
    NSInteger count = self.dataArray.count;
    [self.dataArray enumerateObjectsUsingBlock:^(CYBImageTitleModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        button.titleLabel.font = FONT(14.f);
        button.adjustsImageWhenHighlighted = NO;
        [button setTitleColor:[UIColor lightGrayColor]
                     forState:UIControlStateNormal];
        [button setTitleColor:Color_Orange
                     forState:UIControlStateSelected];
        [button setBackgroundImage:obj.image
                          forState:UIControlStateNormal];
        [button setBackgroundImage:obj.selectedImage
                          forState:UIControlStateSelected];
        [button setTitle:obj.title
                forState:UIControlStateNormal];
        button.tag = kBTN_TAG + idx;
        button.selected = obj.isSelected;
        if (obj.isSelected) {
            tempBtn = button;
        }
        [button wb_addTarget:self action:@selector(buttonClicked:)];
        
        [self.contentView addSubview:button];

        CGFloat titleWidth = [obj.title boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, 28)
                                                     options:NSStringDrawingUsesLineFragmentOrigin
                                                  attributes:@{NSFontAttributeName : FONT(14.f)} context:nil].size.width + 2 * 8;
        rowWidth += titleWidth + spacing;
        /** < 是否需要換行 >  */
        if (rowWidth > maxWidth - 2 * margin) {
            isNeedChangeLine = YES;
            /** < 判斷是否超過(guò)最大值 >  */
            if (titleWidth + 2 * margin > maxWidth) {
                titleWidth = maxWidth - 2 * margin;
            }
            /** < 換行后重新設(shè)置當(dāng)前行的總寬度 >  */
            rowWidth = titleWidth + spacing;
        }
        
        [button mas_makeConstraints:^(MASConstraintMaker *make) {
            /** < 換行 >  */
            if (isNeedChangeLine) {
                if (!lastButton) {
                    make.top.equalTo(self.contentView.mas_top).offset(margin);
                }else {
                    make.top.equalTo(lastButton.mas_bottom).offset(spacing);
                }
                make.left.equalTo(self.contentView.mas_left).offset(margin);
                isNeedChangeLine = NO;
            }else {
                make.left.equalTo(lastButton.mas_right).offset(spacing);
                make.top.equalTo(lastButton.mas_top);
            }
            make.height.mas_equalTo(@(28));
            make.width.mas_equalTo(@(titleWidth));
            
            /** < 最后一個(gè) >  */
            if (idx == count - 1) {
                make.bottom.equalTo(self.contentView.mas_bottom).offset(-margin);
            }
        }];
        lastButton = button;
    }];
}

二、網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)高度自適應(yīng)

  • 后來(lái)改需求了,需要從網(wǎng)絡(luò)請(qǐng)求意見(jiàn)反饋類(lèi)型,好吧,上面的方法已經(jīng)有實(shí)現(xiàn)高度自適應(yīng)關(guān)鍵代碼了,只要稍作修改就可實(shí)現(xiàn)了。但是實(shí)現(xiàn)過(guò)程并不是想象中那么簡(jiǎn)單,中間也經(jīng)理了很多波折。因?yàn)闀r(shí)間還是很充裕的,我就考慮到將標(biāo)簽空間封裝成一個(gè)視圖,等要使用的時(shí)候自己添加到cell上,并設(shè)置上下左右約束,封裝完成之后并沒(méi)有達(dá)到我想要的效果,我發(fā)現(xiàn)cell根本就撐不起來(lái),我檢查了一遍約束,上下左右約束沒(méi)有遺漏呀,封裝的視圖WBAutoTagListView核心代碼如下,約束實(shí)在layoutSubviews設(shè)置的:
#pragma mark < Layout >
- (void)layoutSubviews {
    [super layoutSubviews];
    
    CGFloat maxWidth = self.bounds.size.width - _secionInset.left - _secionInset.right;
    __block CGFloat rowWidth = 0;
    __block BOOL isNeedChangeLine = YES;
    __block WBTagListItem *lastItem = nil;
    NSInteger count = self.itemArray.count;
    [self.itemArray enumerateObjectsUsingBlock:^(WBTagListItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        CGFloat titleWidth = obj.titleWidth;
        rowWidth += titleWidth + _minimumInteritemSpacing;
         /** < 是否需要換行 >  */
        if (rowWidth > maxWidth - 2 * _minimumInteritemSpacing) {
            isNeedChangeLine = YES;
            /** < 判斷是否超過(guò)最大值 >  */
            if (titleWidth + 2 * _minimumInteritemSpacing > maxWidth) {
                titleWidth = maxWidth - 2 * _minimumInteritemSpacing;
            }
            /** < 換行后重新設(shè)置當(dāng)前行的總寬度 >  */
            rowWidth = titleWidth + _minimumInteritemSpacing;
        }
        [obj mas_makeConstraints:^(MASConstraintMaker *make) {
            /** < 換行 >  */
            if (isNeedChangeLine) {
                if (!lastItem) {
                    make.top.equalTo(self.mas_top).offset(_secionInset.top);
                }else {
                    make.top.equalTo(lastItem.mas_bottom).offset(_minimumLineSpacing);
                }
                make.left.equalTo(self.mas_left).offset(_secionInset.left);
                isNeedChangeLine = NO;
            }else {
                make.left.equalTo(lastItem.mas_right).offset(_minimumInteritemSpacing);
                make.top.equalTo(lastItem.mas_top);
            }
            make.height.mas_equalTo(@(_itemHeight));
            make.width.mas_equalTo(@(titleWidth));
            
            /** < 最后一個(gè) >  */
            if (idx == count - 1) {
                make.bottom.mas_offset(-_secionInset.bottom).priorityMedium();
            }
        }];
        lastItem = obj;
    }];
    NSLog(@"%f",[self systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height);
}
  • 經(jīng)測(cè)試,將該視圖添加到控制器的視圖上是可以自適應(yīng)高度的,但是添加的cell上,就無(wú)法撐cell高度,嘗試了許多寫(xiě)法,還是未能實(shí)現(xiàn),控制臺(tái)提示了無(wú)法算出cell的高度,就給了一個(gè)默認(rèn)高度,頓時(shí)都無(wú)語(yǔ)了,有知道的大神能告訴我為什么有內(nèi)容卻無(wú)法撐起cell高度嗎?
  • 既然封裝的視圖無(wú)法實(shí)現(xiàn)cell高度自適應(yīng),我就嘗試另外的思路方法,既然是cell自適應(yīng),那就索性封裝一個(gè)標(biāo)簽cell吧WBTagListViewCell,為了可復(fù)用性,也為WBTagListViewCell添加了一些配置屬性,如下:
/** < 數(shù)據(jù)源 > */
@property (nonatomic, strong) NSArray <WBTagListModel *>*items;
/** < 內(nèi)邊距 默認(rèn): UIEdgeInsetsMake(15, 15, 15, 15) > */
@property (nonatomic, assign) UIEdgeInsets secionInset;
/** < 行間距 默認(rèn):15 > */
@property (nonatomic, assign) CGFloat minimumLineSpacing;
/** < item之間距離 默認(rèn):10 > */
@property (nonatomic, assign) CGFloat minimumInteritemSpacing;
/** < 是否允許點(diǎn)擊 默認(rèn):YES > */
@property (nonatomic, assign) BOOL allowSelection;
/** < 是否允許多選 默認(rèn):NO > */
@property (nonatomic, assign) BOOL allowMultipleSelection;
/** < 標(biāo)簽高度 默認(rèn):28.f > */
@property (nonatomic, assign) CGFloat itemHeight;

/** < 標(biāo)簽左右間距 默認(rèn):10.f > */
@property (nonatomic, assign) CGFloat leftRightMargin;
/** < 背景圖片 > */
@property (nonatomic, copy) NSString *bgImageName;
/** < 選中背景圖片 > */
@property (nonatomic, copy) NSString *selectedBgImageName;
/** < 標(biāo)簽顏色 默認(rèn):淺灰色 > */
@property (nonatomic, strong) UIColor *titleColor;
/** < 按鈕選中顏色 > */
@property (nonatomic, strong) UIColor *titleSelectedColor;
/** < 標(biāo)題大小 默認(rèn):14pt > */
@property (nonatomic, strong) UIFont *font;
/** < 邊框?qū)挾?默認(rèn):0 > */
@property (nonatomic, assign) CGFloat borderWidth;
/** < 邊框顏色 bodoerWidth > 0 設(shè)置有效 > */
@property (nonatomic, strong) UIColor *borderColor;
/** < 選中邊框顏色 bodoerWidth > 0 設(shè)置有效 > */
@property (nonatomic, strong) UIColor *selectedBorderColor;
/** < 圓角大小 默認(rèn):0 > */
@property (nonatomic, assign) CGFloat cornerRadius;
/** < 選中的item > */
@property (nonatomic, strong) NSMutableArray *selectedItems;

@property (nonatomic, weak) id <WBTagListViewCellDelegate> delegate;
  • 關(guān)鍵實(shí)現(xiàn)步驟是重寫(xiě)了cell的updateConstraints,在有數(shù)據(jù)源的時(shí)候調(diào)用setNeedsUpdateConstraints,關(guān)鍵代碼如下:
- (void)createTagWithData:(NSArray <WBTagListModel *>*)itemsArray {
    for (UIView *view in self.itemArray) {
        [view removeFromSuperview];
    }
    [self.itemArray removeAllObjects];
    
    for (NSInteger index = 0; index < itemsArray.count; index ++) {
        WBTagListItem *item = [WBTagListItem new];
        item.title = itemsArray[index].title;
        item.isSelected = itemsArray[index].isSelected;
        item.itemTag = index;
        item.delegate = self;
        [self.contentView addSubview:item];
        [self.itemArray addObject:item];
        
        /** < 默認(rèn)選中第一個(gè) > */
        if (index == 0 && itemsArray[index].isSelected) {
            _tempItem = item;
            [self.selectedItems removeAllObjects];
            [self.selectedItems addObject:_tempItem];
        }
    }
    [self setNeedsUpdateConstraints];
}

- (void)updateConstraints {
    [super updateConstraints];
    
    CGFloat maxWidth = self.contentView.bounds.size.width - _secionInset.left - _secionInset.right;
    __block CGFloat rowWidth = 0;
    __block BOOL isNeedChangeLine = YES;
    __block WBTagListItem *lastItem = nil;
    NSInteger count = self.itemArray.count;
    [self.itemArray enumerateObjectsUsingBlock:^(WBTagListItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        CGFloat titleWidth = obj.titleWidth;
        rowWidth += titleWidth + _minimumInteritemSpacing;
        /** < 是否需要換行 >  */
        if (rowWidth > maxWidth - 2 * _minimumInteritemSpacing) {
            isNeedChangeLine = YES;
            /** < 判斷是否超過(guò)最大值 >  */
            if (titleWidth + 2 * _minimumInteritemSpacing > maxWidth) {
                titleWidth = maxWidth - 2 * _minimumInteritemSpacing;
            }
            /** < 換行后重新設(shè)置當(dāng)前行的總寬度 >  */
            rowWidth = titleWidth + _minimumInteritemSpacing;
        }
        [obj mas_makeConstraints:^(MASConstraintMaker *make) {
            /** < 換行 >  */
            if (isNeedChangeLine) {
                if (!lastItem) {
                    make.top.equalTo(self.contentView.mas_top).offset(_secionInset.top);
                }else {
                    make.top.equalTo(lastItem.mas_bottom).offset(_minimumLineSpacing);
                }
                make.left.equalTo(self.contentView.mas_left).offset(_secionInset.left);
                isNeedChangeLine = NO;
            }else {
                make.left.equalTo(lastItem.mas_right).offset(_minimumInteritemSpacing);
                make.top.equalTo(lastItem.mas_top);
            }
            make.height.mas_equalTo(@(_itemHeight));
            make.width.mas_equalTo(@(titleWidth));
            
            /** < 最后一個(gè) >  */
            if (idx == count - 1) {
                make.bottom.equalTo(self.contentView.mas_bottom).offset(-_secionInset.bottom).priorityMedium();
            }
        }];
        lastItem = obj;
    }];
}
  • 最后運(yùn)行效果也貼一張圖吧


    Simulator Screen Shot - iPhone X - 2018-06-07 at 23.48.33.png
  • 封裝cell在實(shí)現(xiàn)過(guò)程中,也遇到一些問(wèn)題,最開(kāi)始把約束寫(xiě)到layoutSubviews還是無(wú)法自適應(yīng)高度,再就是要考慮到cell復(fù)用的問(wèn)題。不管怎樣最后還是實(shí)現(xiàn)了自己想要的效果,由于技術(shù)有限,可能我有寫(xiě)的不對(duì)不好的地方,還請(qǐng)斧正。最后也貼出自動(dòng)布局和frame布局標(biāo)簽demo,如果覺(jué)得對(duì)你有幫助,請(qǐng)Star鼓勵(lì)下吧。

三、GitHub Demo

Auto:WBAutoTagListViewDemo
Frame:WB_TagsViewDemo

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容