多級列表嵌套頁面如何做到不包含任何手動計算進行自動布局并獲取嵌套的UI高度
如上圖所示。看似很復雜的頁面,其中cell上的label高度不定,各種嵌套,或一個tableView難以應對業務時候我們應該如何做到自動布局和計算(全程不包含任何計算)
1.首先上圖所示,sectionHeader,sectionFooter,cell子控件元素較多,設計到label根據文字撐開高度。如果采用frame布局,根據上下文計算label高度,形成一個個組件那么需要計算多個組件的高度,且每一個label均需要計算
這時候采用masonry自動布局
如何做到使用masonry自動布局sectionHeader,sectionFooter,cell,cell嵌套tableView且不用任何計算呢
下面注意使用方法和細節:
1.sectionHeader/sectionFooter基于masonry的自動計算
1)首先tableView的創建需要增加
_tableView.estimatedSectionHeaderHeight = 500;//sectionHeader預估高度
_tableView.sectionHeaderHeight = UITableViewAutomaticDimension;//sectionHeader自動計算
_tableView.estimatedRowHeight = 200;//cell預估高度
?_tableView.rowHeight = UITableViewAutomaticDimension;//cell自動計算
_tableView.estimatedSectionFooterHeight = 300;//sectionFooter預估高度
_tableView.sectionFooterHeight = UITableViewAutomaticDimension;//sectionFooter自動計算
2)注冊你的sectionheader和sectionfooter,
?[_tableView registerClass:[@“你的header/footer” class] forHeaderFooterViewReuseIdentifier:@"唯一標識符"];
3)特別強調,header/footer均自定義,且必須繼承于UITableViewHeaderFooterView。知識點tips1:很多開發者習慣繼承于UIView自定義section的header和footer是不友好的,因為view不具備復用機制,UITableViewHeaderFooterView有復用機制,繼承UIView點開圖層會發現View在頂部疊加。
自定義好的UITableViewHeaderFooterView內部需要重寫下述方法并在內部處理UI及交互,且內部元素布局均為masonry,注意約束到底部,嚴格避免約束沖突
- (instancetype)initWithReuseIdentifier:(NSString*)reuseIdentifier {
? ? if(self= [superinitWithReuseIdentifier:reuseIdentifier]) {
?? ?}
?? ?return self;
}
4)tableView的代理只需要實現下面方法,高度設置的代理方法不再添加
-(UIView*)tableView:(UITableView*)tableViewviewForHeaderInSection:(NSInteger)section {
? 你的View *view = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"唯一標識符"];
? returnview;
}
5.cell/footer 原理同上
6.難點:cell上嵌套的tableView,masonry約束布局,嵌套的tableView的cell也是基于masonry自動布局,如何獲取tableView的高度
1.第一種方案:tableView/collectionView均繼承于UIScrollView,我們可以通過KVO監聽scorllView的contentSize方式來獲取tableView撐開后的偏移量,及為布局結束后的高度值
難點: NSRunLoop檢測布局更新有延遲需要刷新喚醒新的runloop更新約束。所以這時候在更新約束完成后無論使用?layoutIfNeeded、setNeedsLayout、setNeedsUpdateConstraints、updateConstraintsIfNeeded、setNeedsDis...都不會生效,因為即便調用了上述更新布局的觸發方式,但是cell的高度已經被第一級tableView拿到了,所以這時候需要我們想辦法去觸發tableView的刷新。避免布局錯亂(建議將tableView傳進來,筆者原基于dispatch_block_t無參回調觸發,發現阻塞線程,你也可以用自定義block嘗試)
性能優化:監聽高度是被多次調用的方法,所以刷新時候采用無感知無動畫方式刷新
///監聽高度
? [_tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath
? ? ? ? ? ? ? ? ? ? ? ofObject:(id)object
? ? ? ? ? ? ? ? ? ? ? ? change:(NSDictionaryid> *)change
?? ? ? ? ? ? ? ? ? ? ? context:(void*)context {
? if([keyPathisEqualToString:@"contentSize"]) {
? ? CGFloatheight =_tableView.contentSize.height;
//? ? NSLog(@"監聽高度----%f",height);
? ? [_tableView mas_updateConstraints:^(MASConstraintMaker *make) {
? ? ? make.height.mas_equalTo(height);
? ? }];
? ? //NSRunLoop檢測布局更新有延遲需要刷新喚醒新的runloop更新約束
? ? kWeakSelf;
? ? [UIView performWithoutAnimation:^{
? ? ? [weakSelf.superTableViewreloadData];
? ? }];
? }
}
第二種方案:
在LayOutSubView中約束tableView,獲取高度的方式為延遲0.1s觸發layOutIfNeeded。
補充一個面試中的冷門知識:tableView的底層API的reloadData有什么缺陷?回答:無法獲知它是否刷新完畢了,如何確保刷新完畢,在reloadData后面追加layOutIfNeeded。
//第二種獲取cell上的tableView的高度方式
//-(CGFloat)getHeightWithTablViewContenSize{
//? kWeakSelf;
//? ? dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//? ? ? ? NSLog(@"==%@", weakSelf.tableView);
//? ? });
//? ? return? _tableView.contentSize.height;
//}
第三種方式:1)https://github.com/CoderJackyHuang/HYBMasonryAutoCellHeight 第三方(沒必要:不建議)
最終效果