前言
好久沒有更新自己的博客了碟刺,去年自己在博客總結(jié)說到自己要堅持更新博客,不管是否有技術(shù)含量除抛,都要持之以恒狮杨,無論做什么,都貴在堅持到忽,我希望通過寫博客能積累自己的技術(shù)與經(jīng)驗橄教。不多說了,言歸正傳喘漏,下面開始介紹我是如何實現(xiàn)Cell標(biāo)簽自適應(yīng)的吧护蝶。
一、本地數(shù)據(jù)自適應(yīng)
- 在做項目意見反饋的時候翩迈,需要選擇反饋類型持灰,整個界面是UITableView,我現(xiàn)在喜歡用自動布局负饲,用的Masonry布局框架堤魁,開始選擇類型是放在本地的,用Masonry實現(xiàn)cell高度自適應(yīng)還算相對簡單的返十,下面是實現(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;
/** < 判斷是否超過最大值 > */
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));
/** < 最后一個 > */
if (idx == count - 1) {
make.bottom.equalTo(self.contentView.mas_bottom).offset(-margin);
}
}];
lastButton = button;
}];
}
二、網(wǎng)絡(luò)請求數(shù)據(jù)高度自適應(yīng)
- 后來改需求了吧慢,需要從網(wǎng)絡(luò)請求意見反饋類型涛漂,好吧,上面的方法已經(jīng)有實現(xiàn)高度自適應(yīng)關(guān)鍵代碼了检诗,只要稍作修改就可實現(xiàn)了匈仗。但是實現(xiàn)過程并不是想象中那么簡單,中間也經(jīng)理了很多波折逢慌。因為時間還是很充裕的悠轩,我就考慮到將標(biāo)簽空間封裝成一個視圖,等要使用的時候自己添加到cell上攻泼,并設(shè)置上下左右約束火架,封裝完成之后并沒有達(dá)到我想要的效果,我發(fā)現(xiàn)cell根本就撐不起來忙菠,我檢查了一遍約束何鸡,上下左右約束沒有遺漏呀,封裝的視圖WBAutoTagListView核心代碼如下牛欢,約束實在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;
/** < 判斷是否超過最大值 > */
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));
/** < 最后一個 > */
if (idx == count - 1) {
make.bottom.mas_offset(-_secionInset.bottom).priorityMedium();
}
}];
lastItem = obj;
}];
NSLog(@"%f",[self systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height);
}
- 經(jīng)測試骡男,將該視圖添加到控制器的視圖上是可以自適應(yīng)高度的,但是添加的cell上傍睹,就無法撐cell高度隔盛,嘗試了許多寫法,還是未能實現(xiàn)拾稳,控制臺提示了無法算出cell的高度吮炕,就給了一個默認(rèn)高度,頓時都無語了访得,有知道的大神能告訴我為什么有內(nèi)容卻無法撐起cell高度嗎龙亲?
- 既然封裝的視圖無法實現(xiàn)cell高度自適應(yīng),我就嘗試另外的思路方法悍抑,既然是cell自適應(yīng)鳄炉,那就索性封裝一個標(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;
/** < 是否允許點擊 默認(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)鍵實現(xiàn)步驟是重寫了cell的updateConstraints,在有數(shù)據(jù)源的時候調(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)選中第一個 > */
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;
/** < 判斷是否超過最大值 > */
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));
/** < 最后一個 > */
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在實現(xiàn)過程中磕仅,也遇到一些問題,最開始把約束寫到layoutSubviews還是無法自適應(yīng)高度簸呈,再就是要考慮到cell復(fù)用的問題榕订。不管怎樣最后還是實現(xiàn)了自己想要的效果,由于技術(shù)有限蜕便,可能我有寫的不對不好的地方劫恒,還請斧正。最后也貼出自動布局和frame布局標(biāo)簽demo,如果覺得對你有幫助两嘴,請Star鼓勵下吧丛楚。
三、GitHub Demo
Auto:WBAutoTagListViewDemo
Frame:WB_TagsViewDemo