为何UITableView各单元格显示相同视图?修复分段控件重复问题
解决UITableView所有单元格重复显示UISegmentedControl的问题
这个问题我太熟了!本质是UITableViewCell的复用机制在搞鬼——你第一次给第一个cell添加了UISegmentedControl之后,这个cell被回收复用给其他行时,控件并没有被移除,所以后续所有单元格都会带着这个控件显示。
给你几个靠谱的修复方案,从临时快速修复到规范工程化实现都有:
方案一:临时快速修复(适合简单场景)
通过给控件加tag,每次复用cell时先移除之前可能存在的segmentedControl,再根据位置决定是否添加:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // 注意:不要把register放在cellForRowAt里!移到viewDidLoad里更高效 let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) // 先移除之前复用cell上残留的segmentedControl cell.contentView.viewWithTag(1001)?.removeFromSuperview() // 这里注意:如果你的需求是「第一个section的第一行」,要改成indexPath.section == 0 && indexPath.row == 0 if indexPath.row == 0 { let genderItems = ["Male", "Female"] let genderControl = UISegmentedControl(items: genderItems) genderControl.tag = 1001 // 设置tag方便后续查找移除 genderControl.addTarget(self, action: #selector(genderChanged), for: .valueChanged) cell.contentView.addSubview(genderControl) genderControl.translatesAutoresizingMaskIntoConstraints = false genderControl.centerXAnchor.constraint(equalTo: cell.centerXAnchor).isActive = true genderControl.centerYAnchor.constraint(equalTo: cell.centerYAnchor).isActive = true } return cell }
同时记得把tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")移到viewDidLoad里,避免每次创建cell都重复注册,浪费资源。
方案二:自定义UITableViewCell(推荐,规范做法)
这是工程里的标准解决方案,把不同类型的cell拆分,从根本上避免复用混乱:
第一步:创建自定义Cell
class GenderSelectTableViewCell: UITableViewCell { // 提前初始化好segmentedControl let genderControl: UISegmentedControl = { let items = ["Male", "Female"] let control = UISegmentedControl(items: items) control.translatesAutoresizingMaskIntoConstraints = false return control }() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupSubviews() } required init?(coder: NSCoder) { super.init(coder: coder) setupSubviews() } private func setupSubviews() { contentView.addSubview(genderControl) // 一次性设置好约束 NSLayoutConstraint.activate([ genderControl.centerXAnchor.constraint(equalTo: contentView.centerXAnchor), genderControl.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) ]) } }
第二步:修改ViewController代码
override func viewDidLoad() { super.viewDidLoad() // 注册两种不同的cell:带选择器的和普通的 tableView.register(GenderSelectTableViewCell.self, forCellReuseIdentifier: "GenderCell") tableView.register(UITableViewCell.self, forCellReuseIdentifier: "NormalCell") } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // 根据位置返回对应的cell类型 if indexPath.section == 0 && indexPath.row == 0 { let cell = tableView.dequeueReusableCell(withIdentifier: "GenderCell", for: indexPath) as! GenderSelectTableViewCell // 这里可以统一设置回调,注意避免重复添加target(可以在cell内部处理或做判断) cell.genderControl.addTarget(self, action: #selector(genderChanged), for: .valueChanged) return cell } else { let cell = tableView.dequeueReusableCell(withIdentifier: "NormalCell", for: indexPath) // 设置普通cell的内容 return cell } }
额外提醒
你当前的代码中numberOfSections返回5,每个section的numberOfRowsInSection返回1,所以如果你的需求是只有第一个section的第一行显示选择器,一定要把判断条件改成indexPath.section == 0 && indexPath.row == 0,不然每个section的第一行都会显示segmentedControl。
内容的提问来源于stack exchange,提问作者Neulio
相关产品推荐
相关产品推荐

