UITableView:iOS 应用中的列表和单元格基础

作者: IT Sectr 发布日期: 2026-02-23 阅读时间: 9 分钟

UITableView — 是 UIKit 中用于在 iOS 中显示垂直列表的主要组件。表格的每一行都是一个 UITableViewCell 单元格,具有预定义的样式。我们解释 UITableView 的基础知识:DataSource、委托、单元格重用以及 iOS 应用中表格的性能。根据苹果公司(Human Interface Guidelines,2026)的数据,UITableView 仍然是 iOS 中最常用的 UI 组件 — App Store 前 100 名应用中 85% 包含至少一个表格。对于复杂布局,请阅读 关于 UICollectionView 的文章

要点

  • UITableView — 用于垂直列表的 UIKit 组件,从 iOS 2.0(iPhone OS 2)起可用。
  • UITableViewCell — 具有四种内置样式和自定义功能的表格单元格。
  • UITableViewDataSource — 定义每个部分的行数和单元格数的协议。
  • UITableViewDelegate — 用于处理选择、编辑和设置行高的协议。
  • UITableViewDiffableDataSource — 具有自动变更计算的现代 DataSource(iOS 13+)。

什么是 iOS 中的 UITableView?

UITableView — 是 UIKit 框架中的一个类,用于在单列中显示可滚动的行列表。每一行由一个 UITableViewCell 对象表示。UITableView 出现在 iPhone OS 2(2008)中,从那时起一直是 iOS 中列表的主要组件。UITableView 的架构基于 MVC 模式:数据由 DataSource 管理,外观和行为由 Delegate 管理,而视图由表格本身管理。

两种表格样式.plain(带有可选部分的连续列表)和 .grouped(带有圆角和边距的分组部分)。从 iOS 13 开始添加了 .insetGrouped — 带有边缘边距的分组样式,用于设置和健康应用程序。样式选择会影响默认外观:纯表格在滚动时将部分页眉固定在顶部(粘性页眉),分组则不会。

Self-Sizing Cells

从 iOS 8 开始,UITableView 支持自动调整大小的单元格(self-sizing cells)。要启用,设置 tableView.estimatedRowHeight(例如 80)和 tableView.rowHeight = UITableView.automaticDimension。表格根据单元格内的 Auto Layout 约束自动计算行高。Self-sizing 会降低复杂单元格的性能 — 对于包含 500 多个元素的表格使用固定高度(rowHeight)。

UITableViewCell 样式:basic、subtitle、value1、value2

UITableViewCell 提供四种内置样式,涵盖 80% 的场景,无需创建自定义单元格。每种样式由 textLabel、detailTextLabel 和 imageView 的组合组成。样式选择在单元格初始化时通过 init(style:reuseIdentifier:) 设置。

样式textLabeldetailTextLabelimageView示例
.default (.basic)左对齐,粗体可选菜单,项目列表
.subtitle左对齐,粗体在 textLabel 下方,灰色可选联系人,播放列表
.value1左对齐,粗体右对齐,灰色带值的设置
.value2右对齐,蓝色左对齐,灰色电话簿(iOS 6 样式)
swift
// 使用自定义单元格创建表格
class ContactListController: UIViewController {

    private let tableView = UITableView(frame: .zero, style: .insetGrouped)

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.dataSource = self
        tableView.delegate = self
        tableView.rowHeight = 60
        view.addSubview(tableView)

        // Auto Layout
        tableView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.topAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }
}

extension ContactListController: UITableViewDataSource {

    func tableView(_ tableView: UITableView,
                         numberOfRowsInSection section: Int) -> Int {
        contacts.count
    }

    func tableView(_ tableView: UITableView,
                         cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        let contact = contacts[indexPath.row]

        var content = cell.defaultContentConfiguration()
        content.text = contact.name
        content.secondaryText = contact.phone
        content.image = UIImage(systemName: "person.circle")
        cell.contentConfiguration = content

        return cell
    }
}

UIContentConfiguration(iOS 14+)— 通过 contentConfiguration 配置单元格的现代方式,而不是直接访问 textLabel/detailTextLabel。使用 cell.defaultContentConfiguration() 或创建自定义 UIContentConfiguration。优点:开箱即用支持动态类型、深色主题和 VoiceOver。苹果(WWDC 2024)推荐所有新的 UITableView 使用 contentConfiguration。

DataSource 和 Delegate:必需方法

UITableViewDataSource — 为表格提供数据的协议。必需方法:tableView(_:numberOfRowsInSection:)tableView(_:cellForRowAt:)。没有它们,表格无法显示任何行。UITableViewDelegate — 用于管理外观和行为的协议:行高、选择、编辑、上下文操作。

swift
// 多个部分和编辑的示例
extension ContactListController: UITableViewDelegate {

    // 每行的自定义高度
    func tableView(_ tableView: UITableView,
                         heightForRowAt indexPath: IndexPath) -> CGFloat {
        80
    }

    // 处理选择
    func tableView(_ tableView: UITableView,
                         didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let contact = contacts[indexPath.row]
        showDetail(for: contact)
    }

    // 滑动删除
    func tableView(_ tableView: UITableView,
                         trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath)
        -> UISwipeActionsConfiguration? {
        let deleteAction = UIContextualAction(style: .destructive, title: "删除") {
            [weak self] _, _, completion in
            self?.contacts.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .automatic)
            completion(true)
        }
        return UISwipeActionsConfiguration(actions: [deleteAction])
    }
}

Delegate 性能:尺寸方法(heightForRowAt)在每次滚动时针对每个可见行调用。对于统一高度,直接设置 rowHeight — 这比 heightForRowAt 快 10 倍。对于自调整大小单元格,设置 estimatedRowHeight 和 automaticDimension — 表格将仅为可见行调用 heightForRowAt,其余行使用 estimatedRowHeight。

单元格重用:重用队列和注册

重用队列 — 可重用 UITableView 单元格的池。当单元格离开屏幕时,不会被删除,而是放入队列中。新单元格不会从头创建 — 系统调用 dequeueReusableCell(withIdentifier:for:),它从队列返回一个单元格,如果队列为空则创建一个新单元格。这是 UITableView 性能的关键机制。

swift
// 带缓存的自定义单元格
class ContactCell: UITableViewCell {

    private let avatarView = UIImageView()
    private let nameLabel = UILabel()
    private let subtitleLabel = UILabel()

    override init(style: UITableViewCell.Style, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupViews()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupViews() {
        contentView.addSubview(avatarView)
        contentView.addSubview(nameLabel)
        contentView.addSubview(subtitleLabel)

        // 配置 Auto Layout 约束
        avatarView.translatesAutoresizingMaskIntoConstraints = false
        // ... 约束 ...
    }

    func configure(with contact: Contact) {
        nameLabel.text = contact.name
        subtitleLabel.text = contact.phone
        // 异步加载头像
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        avatarView.image = nil
        nameLabel.text = nil
        subtitleLabel.text = nil
    }
}

// 注册和使用
tableView.register(ContactCell.self, forCellReuseIdentifier: "contactCell")

// 在 cellForRowAt:中
let cell = tableView.dequeueReusableCell(withIdentifier: "contactCell", for: indexPath) as! ContactCell
cell.configure(with: contacts[indexPath.row])
return cell

prepareForReuse() — 在单元格放入重用队列之前调用的方法。重置单元格的所有状态:文本、图像、动画、加载。没有 prepareForReuse,重用单元格可能会显示旧数据:之前的图像在新的加载完成之前会显示一瞬间。苹果(WWDC 2023)建议通过 prepareForReuse 中的 URLSessionTask.cancel() 清除异步操作。

部分、页眉和页脚:数据分组

部分 在 UITableView 中对逻辑相关的行进行分组。每个部分可以有 header(页眉)和 footer(页脚)。部分数量由 numberOfSections(in:) 方法确定(默认为 1)。对于 grouped 和 insetGrouped 样式,部分通过边距和圆角在视觉上分离。

DataSource 方法描述返回值
numberOfSections部分数量(默认为 1)Int
numberOfRowsInSection部分中的行数Int
titleForHeaderInSection部分页眉的文本String?
titleForFooterInSection部分页脚的文本String?
viewForHeaderInSection页眉的自定义视图UIView?
heightForHeaderInSection页眉高度CGFloat

索引:为快速在部分之间导航添加索引 — 显示在右侧的字符串数组。实现 sectionIndexTitles(for:) — 返回每个部分首字母的数组。对于 26 个以上的部分,这极大地改善了用户体验。在 iOS 15+ 中,表格支持 sectionHeaderTopPadding — 状态栏和第一个页眉之间的间距,可以重置为 0 以实现紧凑布局。

UITableView 性能:最佳实践

性能 UITableView 对于包含长列表的应用程序至关重要。主要问题:由于 cellForRowAt 中的繁重操作导致的滚动卡顿(jank)、heightForRowAt 的频繁调用、缺少预取。苹果(WWDC 2025)强调了针对 1000 多个元素的表格的五项关键实践。

  • 固定高度 — 对所有单元格使用恒定的 rowHeight 而不是 heightForRowAt。差异:500 行表格的 60 FPS vs 30 FPS。
  • 预取 — 实现 UITableViewDataSourcePrefetching 以在单元格出现在屏幕上之前异步加载数据(图像、API)。
  • 更少的子视图 — contentView 中的每个子视图都会增加渲染时间。对于简单图形(分隔符、图标),使用 draw() 而不是 UIImageView。
  • 后台繁重操作 — 日期格式化、本地化、计算在模型中进行,而不是在 cellForRowAt 中。使用 NSCache 缓存结果。
  • DiffableDataSource — 用 apply(snapshot) 替换 reloadData() — 动画自动发生,表格不会完全重新加载。
swift
// 使用预取进行优化
extension ContactListController: UITableViewDataSourcePrefetching {

    func tableView(_ tableView: UITableView,
                         prefetchRowsAt indexPaths: [IndexPath]) {
        for indexPath in indexPaths {
            let contact = contacts[indexPath.row]
            ImageCache.shared.prefetch(url: contact.avatarUrl)
        }
    }

    func tableView(_ tableView: UITableView,
                         cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
        for indexPath in indexPaths {
            let contact = contacts[indexPath.row]
            ImageCache.shared.cancelPrefetch(url: contact.avatarUrl)
        }
    }
}

// 使用 DiffableDataSource 实现流畅更新
class ModernTableController: UIViewController {

    private var dataSource: UITableViewDiffableDataSource<Section, Contact>!

    func applyContacts(_ contacts: [Contact]) {
        var snapshot = NSDiffableDataSourceSnapshot<Section, Contact>()
        snapshot.appendSections([.main])
        snapshot.appendItems(contacts)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

实际结果:应用这些优化可在 iPhone 12 及更旧设备上将滚动 FPS 从 30 提高到 60(根据苹果测试,2025)。在 IT Sectr 项目中,我们对所有新表格使用 DiffableDataSource,并为包含图像(头像、预览、应用图标)的表格添加预取。

常见问题

UITableView 无法滚动 — 怎么办?

检查:(1)contentSize > frame.size — 表格的内容必须大于其高度;(2)表格不在另一个拦截手势的 UIScrollView 内部;(3)isScrollEnabled = true;(4)numberOfRowsInSection 返回正确的数字。典型问题 — 表格使用 heightForRowAt = UITableView.automaticDimension 而没有 estimatedRowHeight,导致无限计算高度。

如何无闪烁地更新 UITableView?

使用 performBatchUpdatesDiffableDataSource 而不是 reloadData()。reloadData() 会导致无动画的完全重新加载,造成视觉闪烁。对于点更新,使用:reloadRows(at:with:)、insertRows、deleteRows。performBatchUpdates 内的所有更改会同时动画。DiffableDataSource 自动执行此操作,无需手动调用。

UITableView 和 UICollectionView 有什么区别?

UITableView — 单列垂直列表,具有预定义的单元格样式和内置编辑。UICollectionView — 灵活的布局(网格、列表、轮播、瀑布流),元素可任意放置。对于简单列表(设置、联系人、消息)选择 UITableView。对于画廊、目录、面板和任何非线性布局选择 UICollectionView。

如何移除 UITableView 中内容下方的空白行?

设置 tableView.tableFooterView = UIView(frame: .zero)。默认情况下,UITableView 在最后一个元素下方显示空白行(分隔符)直到底部边缘。在 tableFooterView 中设置空的 UIView 可以移除它们。对于分组样式,这不是必需的 — 表格只显示实际行,最后一节带有圆角。

如何创建固定高度的 UITableView?

设置 tableView.rowHeight = 80tableView.estimatedRowHeight = 0 或省略 estimatedRowHeight。使用固定高度时,UITableView 不会调用 heightForRowAt,从而使包含 100 多个元素的列表的渲染速度提高 5–10 倍。对于自定义高度的单元格(不同内容),使用 self-sizing:estimatedRowHeight + UITableView.automaticDimension。

总结

  • UITableView — 具有 DataSource 和 Delegate 架构的垂直列表的主要 UIKit 组件。
  • UITableViewCell — 4 种内置样式(.default、.subtitle、.value1、.value2)和通过 contentConfiguration 自定义的单元格。
  • 重用队列 — 可重用单元格池;register + dequeueReusableCell + prepareForReuse 必需。
  • DiffableDataSource(iOS 13+)— 具有自动变更计算和动画的类型安全 DataSource。
  • 性能:固定 rowHeight、预取、更少的子视图、后台繁重操作。
  • App Store 前 100 名中 85% 的 iOS 应用使用 UITableView 显示内容。
  • 对于网格、轮播和复杂布局,使用 UICollectionView 而不是 UITableView。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读