一、前言
UITableView 的主要优势在于:
- 极简的列表实现 - 几行代码就能创建功能完整的列表
- 最佳性能 - 专门为垂直列表优化
- 丰富的内置功能 - 编辑模式、索引、刷新控制等
- 成熟的生态系统 - 大量第三方库和社区支持
对于简单的垂直列表需求,UITableView 通常是最简单、最高效的选择。
二、示例代码
简单效果如下

//
// CustomUITabView.swift
// UiKitExample
//
// Created by YM on 9/29/25.
// 这是一个练习UIKit中UITabView的使用方式
// MARK: - UITableView Selection API
//
// 【单选模式】
// • 设置: allowsMultipleSelection = false (默认)
// • 获取: tableView.indexPathForSelectedRow
//
// 【多选模式】
// • 设置: allowsMultipleSelection = true
// • 获取: tableView.indexPathsForSelectedRows
//
// 【编程操作】
// • 选中: selectRow(at:animated:scrollPosition:)
// • 取消: deselectRow(at:animated:)
import UIKit
// 数据模型
struct Fruit {
let name: String
let description: String
let icon: String
}
class FruitCell: UITableViewCell {
let iconImageView = UIImageView()
let titleLabel = UILabel()
let subtitleLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
func setupUI() {
// 配置图标
iconImageView.contentMode = .scaleAspectFit
iconImageView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(iconImageView)
// 配置标题
titleLabel.font = .systemFont(ofSize: 17, weight: .semibold)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(titleLabel)
// 配置副标题
subtitleLabel.font = .systemFont(ofSize: 14)
subtitleLabel.textColor = .gray
subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(subtitleLabel)
// 布局约束
NSLayoutConstraint.activate([
iconImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15),
iconImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
iconImageView.widthAnchor.constraint(equalToConstant: 40),
iconImageView.heightAnchor.constraint(equalToConstant: 40),
titleLabel.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: 15),
titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12),
titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15),
subtitleLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4),
subtitleLabel.trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor)
])
}
func configure(with fruit: Fruit) {
iconImageView.image = UIImage(systemName: fruit.icon)
titleLabel.text = fruit.name
subtitleLabel.text = fruit.description
}
}
class CustomTabView: UIView{
private let tableView = {
let tableView = UITableView()
// tableView.separatorStyle = .none
tableView.backgroundColor = .clear
tableView.separatorStyle = .singleLine
tableView.allowsSelection = true // 允许选择(默认就是 true)
tableView.allowsMultipleSelection = false // 不允许多选(默认)
tableView.separatorInset = UIEdgeInsets(top: 0, left: 70, bottom: 0, right: 0)
tableView.translatesAutoresizingMaskIntoConstraints = false
return tableView
}()
// let data = ["苹果", "香蕉", "橙子", "葡萄", "西瓜", "草莓", "芒果", "樱桃"]
let fruits = [
Fruit(name: "苹果", description: "富含维生素C", icon: "apple.logo"),
Fruit(name: "香蕉", description: "高钾水果", icon: "banana"),
Fruit(name: "橙子", description: "酸甜可口", icon: "circle.fill"),
Fruit(name: "葡萄", description: "营养丰富", icon: "circle.grid.cross"),
Fruit(name: "西瓜", description: "夏季解暑", icon: "drop.fill"),
Fruit(name: "草莓", description: "美容养颜", icon: "heart.fill"),
]
override init(frame: CGRect) {
super.init(frame: frame)
setupTableView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
func setupTableView() {
// 设置代理和数据源
tableView.dataSource = self
tableView.delegate = self
// 注册 cell(使用系统默认的 UITableViewCell)
// tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.register(FruitCell.self, forCellReuseIdentifier: "cell")
addSubview(tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: topAnchor),
tableView.leadingAnchor.constraint(equalTo: leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}
}
extension CustomTabView: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// data.count
fruits.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FruitCell
// cell.textLabel?.text = data[indexPath.row]
// cell.textLabel?.textColor = UIColor.white
// cell.backgroundColor = .clear // Cell 背景透明
cell.configure(with: fruits[indexPath.row])
// 自定义选中背景
// let selectedView = UIView()
// selectedView.backgroundColor = UIColor.white.withAlphaComponent(0.3)
// cell.selectedBackgroundView = selectedView
// cell.accessoryType = .disclosureIndicator
cell.accessoryType = .checkmark
return cell
}
}
extension CustomTabView: UITableViewDelegate {
// 选中时候调用
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 检查是否是当前选中的行,通过判断当前点击是选中还是取消来进行下一步状态更改
// let isSelected = tableView.indexPathForSelectedRow == indexPath
let isSelected = tableView.indexPathsForSelectedRows?.contains(indexPath) ?? false
// print("当前状态: \(isSelected ? "选中" : "未选中")")
// print("选中了: \(data[indexPath.row])")
print("当前点击了: \(fruits[indexPath.row].name)-->此时选择状态为:\(isSelected)")
// 可以在这里跳转到详情页
// let alert = UIAlertController(title: "提示", message: "你选择了\(data[indexPath.row])", preferredStyle: .alert)
// alert.addAction(UIAlertAction(title: "确定", style: .default))
//// present(alert, animated: true)
// 单选模式:获取选中的行
if let selectedRow = tableView.indexPathForSelectedRow {
print("当前选中索引: \(selectedRow.row)")
}
// 获取选中的索引
// 多选模式下,获取所有选中的索引
// if let selectedIndexPaths = tableView.indexPathsForSelectedRows {
// let selectedItems = selectedIndexPaths.map { data[$0.row] }
// print("选中项: \(selectedItems)")
// }
// 选中某一行
// tableView.selectRow(at: IndexPath(row: 0, section: 0), animated: true, scrollPosition: .none)
//
// // 取消选中某一行
// tableView.deselectRow(at: IndexPath(row: 0, section: 0), animated: true)
//
// // 取消所有选中
// if let selectedRows = tableView.indexPathsForSelectedRows {
// for indexPath in selectedRows {
// tableView.deselectRow(at: indexPath, animated: true)
// }
// }
// tableView.deselectRow(at: indexPath, animated: true) // 取消选中效果
}
// ❌ 取消选中时调用
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
print("❌ 取消选中: \(fruits[indexPath.row].name)")
print("此时选择状态为: false")
// 获取剩余选中的行
if let selectedRows = tableView.indexPathsForSelectedRows {
print("当前选中的行数: \(selectedRows.count)")
} else {
print("当前没有选中的行")
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 70
}
}
3048

被折叠的 条评论
为什么被折叠?



