如何解决UITableView滚动后数据重叠的问题
UITableView滚动后单元格数据重叠的解决方案
问题原因
你当前的代码每次调用cellForRowAt时,都会给复用的单元格新增一套UILabel、UIImageView和StackView,但原来单元格上的旧视图并没有被移除。当UITableView滚动复用单元格时,新旧视图叠在一起,就出现了数据重叠现象。
方法一:自定义UITableViewCell(推荐,规范易维护)
这种方式把单元格的视图布局和数据更新逻辑封装在一起,是iOS开发的标准做法。
1. 创建自定义Cell类
import UIKit class UserTableViewCell: UITableViewCell { // 声明需要的控件为cell的属性 private let userProfilePic = UIImageView() private let userNameLabel = UILabel() private let userStackView = UIStackView() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupCellUI() } required init?(coder: NSCoder) { super.init(coder: coder) setupCellUI() } // 一次性完成视图布局配置 private func setupCellUI() { // 配置头像样式 userProfilePic.layer.borderWidth = 1.0 userProfilePic.layer.masksToBounds = false userProfilePic.layer.borderColor = WhistleColors.primaryBorderColor.cgColor userProfilePic.layer.cornerRadius = 20 // 固定为20,对应40x40的尺寸 userProfilePic.clipsToBounds = true userProfilePic.contentMode = .scaleAspectFill // 配置用户名标签 userNameLabel.font = userNameLabel.font.withSize(10) userNameLabel.textAlignment = .center // 配置栈视图 userStackView.addArrangedSubview(userProfilePic) userStackView.addArrangedSubview(userNameLabel) userStackView.alignment = .center userStackView.distribution = .fillEqually userStackView.axis = .vertical // 添加到cell的contentView并设置约束 contentView.addSubview(userStackView) userStackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ userStackView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor, constant: contentView.frame.size.width / 2), userStackView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) ]) // 固定头像尺寸 userProfilePic.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ userProfilePic.widthAnchor.constraint(equalToConstant: 40), userProfilePic.heightAnchor.constraint(equalToConstant: 40) ]) } // 提供更新数据的方法 func update(with user: YourUserModel) { // 把YourUserModel替换成你实际的用户模型类型 userNameLabel.text = user.userFirstName userProfilePic.image = user.profilePicImage } }
2. 注册自定义Cell
如果是纯代码创建的UITableView,在viewDidLoad里注册:
override func viewDidLoad() { super.viewDidLoad() tableView.register(UserTableViewCell.self, forCellReuseIdentifier: "cell") }
如果是用Storyboard/XIB创建的Cell,只需在Storyboard中把Cell的类设置为UserTableViewCell,复用标识符保持为"cell"即可。
3. 修改cellForRowAt方法
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UserTableViewCell let currentUser = activeUsers[indexPath.row] cell.update(with: currentUser) return cell }
方法二:临时快速修复(不推荐,仅用于测试)
如果不想自定义Cell,可以在复用单元格时先移除所有旧子视图,但这种方式代码冗余,性能较差:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) // 先移除contentView上的所有旧视图 cell.contentView.subviews.forEach { $0.removeFromSuperview() } // 以下是你原来的代码(注意要添加到contentView而非cell本身) cell.textLabel?.lineBreakMode = .byWordWrapping let userNameLabel = UILabel() userNameLabel.text = activeUsers[indexPath.row].userFirstName userNameLabel.font = userNameLabel.font.withSize(10) userNameLabel.sizeToFit() let userProfilePic = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40)) userProfilePic.image = activeUsers[indexPath.row].profilePicImage userProfilePic.layer.borderWidth = 1.0 userProfilePic.layer.masksToBounds = false userProfilePic.layer.borderColor = WhistleColors.primaryBorderColor.cgColor userProfilePic.layer.cornerRadius = userProfilePic.frame.size.height / 2 userProfilePic.clipsToBounds = true userProfilePic.contentMode = .scaleAspectFill let userStackView = UIStackView(arrangedSubviews: [userProfilePic, userNameLabel]) userStackView.alignment = .center userStackView.distribution = .fillEqually userStackView.axis = .vertical cell.contentView.addSubview(userStackView) userStackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ userStackView.centerXAnchor.constraint(equalTo: cell.contentView.centerXAnchor, constant: cell.contentView.frame.size.width / 2), userStackView.centerYAnchor.constraint(equalTo: cell.contentView.centerYAnchor) ]) userProfilePic.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ userProfilePic.widthAnchor.constraint(equalToConstant: 40), userProfilePic.heightAnchor.constraint(equalToConstant: 40) ]) return cell }
关键说明
- 自定义Cell是长期维护的最优解,它避免了重复创建视图和约束,代码结构更清晰。
- 无论哪种方法,都应该把自定义视图添加到
cell.contentView而非cell本身,因为contentView是苹果设计用来承载自定义内容的容器。
内容的提问来源于stack exchange,提问作者BiggRojo
相关产品推荐
相关产品推荐

