You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 08:05:22