为何UITableView第一与第二个cell间出现间隙(仅iPhone 11复现)
问题根因
该1px间隙和iPhone11的专属适配无关,核心是圆角实现时机错误+像素对齐误差,刚好在iPhone11的2x缩放屏幕上被触发:
- 你在
cellForRowAt中调用圆角方法时,cell尚未完成最终布局,此时获取的bounds是自动布局计算的中间值,存在非整数的坐标/尺寸偏差 - 生成mask路径时如果使用了带0.5px级小数的rect,CoreAnimation渲染时会做像素舍入,不同屏幕缩放倍数的设备舍入结果存在差异:iPhone11为@2x屏,刚好将误差放大为1个物理像素的可见缝隙,本质是mask层的底边比cell实际高度少1px,露出了tableView的背景。
- 现有代码还存在复用逻辑漏洞:cell被滚动复用到非首行位置时,原有mask不会被移除,会导致其他位置的cell错误带上顶部圆角。
现有实现的问题
直接给cell本身加局部圆角的方式不属于规范实现,除了上述缝隙、复用异常问题外,运行时动态生成CAShapeLayer mask还会增加GPU渲染负担,导致列表滑动掉帧。
修复方案
快速修复(解决现有1px缝隙问题)
将圆角逻辑从cellForRowAt移到cell的layoutSubviews中执行,保证获取的bounds是布局完成的最终值,同时对rect做像素对齐处理,补全复用场景的mask重置逻辑:
class CustomCell: UITableViewCell { private var needTopCorner = false private var cornerRadius: CGFloat = 0 func setTopCorner(radius: CGFloat) { needTopCorner = true cornerRadius = radius setNeedsLayout() } func resetCorner() { needTopCorner = false layer.mask = nil } override func layoutSubviews() { super.layoutSubviews() guard needTopCorner else { return } // 对bounds做像素对齐,消除半像素误差 let pixelAlignedBounds = bounds.integral let maskPath = UIBezierPath( roundedRect: pixelAlignedBounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: cornerRadius, height: cornerRadius) ) let maskLayer = CAShapeLayer() maskLayer.path = maskPath.cgPath layer.mask = maskLayer } }
对应修改cellForRowAt逻辑,仅做cell配置,不直接操作布局属性:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as? CustomCell else { fatalError("The dequeued cell is not an instance of CustomCell.") } if indexPath.row == 0 { cell.setTopCorner(radius: cR) } else { cell.resetCorner() } return cell }
标准实现(对齐Apple Music效果)
如果要完全实现Apple Music式的分组圆角列表,不推荐给单个cell加mask:
- iOS13及以上系统直接使用
UITableView.Style.insetGrouped样式,原生自带分组圆角、边距、分割线适配,效果和系统音乐App完全一致,无需手动处理圆角 - 若需要兼容更低系统版本,建议将圆角效果加在section的自定义背景view上,而非单个cell,从根源上避免cell拼接产生的缝隙问题。

补充说明:该问题并非iPhone11独有,所有@2x分辨率的设备(iPhone XR、iPhone SE2/SE3等)只要触发半像素计算误差都可能出现同类缝隙,之前测试未复现只是场景覆盖不全。
内容的提问来源于stack exchange,提问作者Abv
相关产品推荐
相关产品推荐

