多级列表嵌套页面如何做到不包含任何手动计算进行自动布局并获取嵌套的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 第三方(没必要:不建议)
最终效果