iOS13+下UITableViewController出现多余空白间距该如何排查解决
问题背景
- 项目为后续版本接入SwiftUI,已移除iOS 12版本适配支持;当前正在开发的新版本已移除激励广告商业模式,暂未使用任何SwiftUI能力。
- 在开展新版本适配、处理系统API废弃问题的过程中,项目内
UITableViewController出现来源不明的多余空白间距。此前适配iOS 12的版本与当前版本table view相关代码无明显改动,当时不存在该类间距异常。 - 问题出现在运行iOS 15.5系统的iPhone XS Max设备上,异常截图如下:

- 已尝试通过修改代码调整table view cell高度,完全无法消除上述多余间距。
相关核心代码
/// Height for the regular cell section header static let headerCellHeight: CGFloat = 40 /// The height of the visual radar cell in current state var visualRadarCellHeight = VisualRadarPortraitModeTableViewCell.visualRadarCellHeightWhenOpen func tableView(_ tableView: UITableView, heightForHeaderInSection sectionNumber: Int) -> CGFloat { let height = RadarTableViewController.headerCellHeight let sections = tableView == tableViewRightSide ? rightSections : leftSections let section = sections[sectionNumber] switch section { case .serviceIsOver: guard isGasStationsLoaded else { return CGFloat.leastNonzeroMagnitude } if ServiceBM.shared.state.isServiceOver { return height } if GasStationManager.shared.mode == .destination { return CGFloat.leastNonzeroMagnitude } else { // If subcription is active or it's ads-based model return ServiceBM.shared.state.isSubscriptionOK || !ServiceBM.shared.isAdsFree() ? CGFloat.leastNonzeroMagnitude : height } case .visualRadar: return CGFloat.leastNonzeroMagnitude case .adBanner: return ServiceBM.shared.displayAds() ? CGFloat.leastNonzeroMagnitude : height case .nearestStation: guard isGasStationsLoaded else { return CGFloat.leastNonzeroMagnitude } return height case .cheaperStations: guard isGasStationsLoaded else { return CGFloat.leastNonzeroMagnitude } guard let _ = ExtendedAppDelegate.instantUsersLocation else { return CGFloat.leastNonzeroMagnitude } return nearbyCheaper.count == 0 ? CGFloat.leastNonzeroMagnitude : height case .kpis: guard isGasStationsLoaded else { return CGFloat.leastNonzeroMagnitude } return height //case .game: return height //case .databaseInfo, .location: break default: break } return height } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let sections = tableView == tableViewRightSide ? rightSections : leftSections let section = sections[indexPath.section] switch section { case .visualRadar: return visualRadarCellHeight case .adBanner: return AdsBannerView.kAdSize.height case .nearestStation, .cheaperStations: return RadarGasStationTableViewCell.cellHeight case .kpis: return RadarGasStationTableViewCell.cellHeight * 1.5 //case .game: return 512 default: return UITableView.automaticDimension } }
优先排查方向与解决经验
按问题出现概率从高到低排序:
- 优先排查iOS 15新增的
sectionHeaderTopPadding默认属性
这是移除旧版本适配后最高发的系统API变更坑:iOS 15及以上版本中UITableView新增了sectionHeaderTopPadding属性,默认值为22pt,iOS 15之前的系统中该间距默认值为0。哪怕通过代理返回了极小的section header高度(比如代码里用的CGFloat.leastNonzeroMagnitude),这个默认的顶部padding依然会生效,产生多余空白。
修复方式很简单,在tableView初始化逻辑中加入如下代码即可:
if #available(iOS 15.0, *) { tableView.sectionHeaderTopPadding = 0 }
- 定位空白所属区域缩小排查范围
临时给所有section header、cell、tableFooterView/tableHeaderView设置不同的高亮背景色,同时打印tableView.adjustedContentInset的数值,先确认空白到底属于哪部分:
- 如果空白属于tableView的内容边距,检查
contentInsetAdjustmentBehavior配置、导航栏/安全区域的自动适配逻辑,确认是否是系统自动添加了多余的contentInset - 如果空白属于section header区域,除了上述
sectionHeaderTopPadding问题,还要检查是否实现了viewForHeaderInSection代理,若实现了该方法却对不需要显示的header返回了空view而非nil,也可能触发系统默认边距 - 如果空白属于cell区域,检查使用
UITableView.automaticDimension的cell约束是否存在冲突,是否因为约束优先级问题导致系统自动计算出了超出预期的高度
检查tableHeaderView/tableFooterView的异常高度
如果代码中动态修改过tableHeaderView/tableFooterView的显隐、约束,哪怕把视图高度设为0,约束更新不及时也可能导致系统计算出异常高度占位,可临时将这两个属性设为nil验证是否是该问题导致。检查分组风格tableView的默认边距
如果tableView使用的是UITableView.Style.grouped或者insetGrouped风格,默认会在列表首尾自动添加边距,可通过返回高度为0的header/footer、实现tableView:heightForFooterInSection:代理返回极小值等方式消除该默认间距。
内容的提问来源于stack exchange,提问作者Stéphane de Luca
相关产品推荐
相关产品推荐

