UITableView使用指南

UITableView使用指南

概述

一般遇到大量相同结构的视图进行同一方向排列时,比如一般app的设置页面、通讯录等,使用UITableView是一个非常合适的方案。

基本介绍

风格样式:

这个属性是UITableView最基本的属性,共有两种

  • UITableViewStylePlain:默认样式

    • plain 模式下,如果 tableview 有多个 section(分区、分组),组与组之间默认是没有间距的。

    • 同时组头或组尾会有 sticky 效果(粘性效果、悬停效果),即表格滚动时组头与组尾会自动停留,而不是跟随单元格一同移动。

    • 同时plain模式下的tableView可以有一个section索引,作为一个bar在table的右边(例如A ~ Z)。你可以点击一个特定的标签,跳转到目标section。

    • 例如:iOS中「通讯录」就是典型应用

  • UITableViewStyleGrouped:分组样式

    • grouped 模式下,如果 tableview 有多个 section,组与组之间默认是有间距的。

    • 在表格滚动的同时组头与组尾会随之滚动、不停留,不会有 sticky 效果(粘性效果、悬停效果)。

    • Group类型默认设置tableView灰色背景色,cell为白色背景色,section外边缘设置浅灰色边框,cell设置浅灰色间隔线

    • 例如:iOS中「设置」就是典型应用

踩坑记录:

在iOS15中,如果使用UITableViewStylePlain样式建立TableView,如果给section添加一个header那么会出现一个现象,就是在这个header上方会出现一个空白区域。这个空白区域是由于在iOS15中UITableView中引入了一个新的属性sectionHeaderTopPadding,这个属性默认是22,如果不需要这块空白,改为0即可,代码如下:

swift 复制代码
 let tableView = UITableView()
 if #available(iOS 15.0, *) {
 tableView.sectionHeaderTopPadding = 0
 } else {
 // Fallback on earlier versions
 }

单元格Cell

UITableView的每行数据都是一个UITableViewCell:

  1. 每个Cell 使用IndexPath来表示位置,换言之UITableView中的数据只有行的概念,没有列的概念
  2. IndexPath又分为section和 * row***,每个section为一组,其中可以包含多个行row。**

关于Cell的重用

需要注意的是 TableView 必须使用重用机制 ,当cell滑出屏幕的时候 cell会被系统回收,在下面cell将要显示时会调用之前回收的cell,这样就不必每次都创建销毁,节省内存同时节省时间,如果不使用重用机制,每次都创建新的cell 是非常消耗内存和性能的

swift 复制代码
 // 必须实作的方法:Cell重用,然后设定Cell的内容
 func tableView(_ tableView: UITableView,
                cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   // 取得 tableView 目前使用的 cell
   let cell = tableView.dequeueReusableCell(
     withIdentifier: "Cell",
     for: indexPath) as UITableViewCell
 ​
   // 设置显示的内容
   if let myLabel = cell.textLabel {
     myLabel.text = "xxxx"
   }
 ​
   return cell
 }

Cell的UI和布局

UITableView中每行数据都是一个UITableViewCell,在这个控件中为了显示更多的信息,iOS已经在其内部设置好了多个子控件以供开发者使用。如果我们查看UITableViewCell的声明文件可以发现在内部有一个UIView控件(contentView,作为其他元素的父控件)两个UILable控件(textLabel、detailTextLabel)、一个UIImage控件(imageView),分别用于容器、显示内容、详情和图片。

如果直接使用UITableViewCell则默认共有四种Style设定:

objectivec 复制代码
 typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
     UITableViewCellStyleDefault,   // 左侧显示textLabel(不显示detailTextLabel),imageView可选(显示在最左边)
     UITableViewCellStyleValue1,    // 左侧显示textLabel、右侧显示detailTextLabel(默认蓝色),imageView可选(显示在最左边)
     UITableViewCellStyleValue2,    // 左侧依次显示textLabel(默认蓝色)和detailTextLabel,imageView可选(显示在最左边)
     UITableViewCellStyleSubtitle   // 左上方显示textLabel,左下方显示detailTextLabel(默认灰色),imageView可选(显示在最左边)
 }; 

Cell的属性

accessoryView

在contentView的右边,还有一个accessoryView,这个View是用来作为功能键使用,在iOS中称为访问器,支持显示不同的图标,点击可以触发不同的事件

这个View的设定是通过设置UITableViewCell中的accesoryType属性完成的:

swift 复制代码
 public enum AccessoryType : Int, @unchecked Sendable {
     case none = 0 // 不显示任何图标
     case disclosureIndicator = 1 // 跳转指示图标
     case detailDisclosureButton = 2 // 内容详情图标和跳转指示图标
     case checkmark = 3 // 勾选图标
     @available(iOS 7.0, *)
     case detailButton = 4 // 内容详情图标 
 }

默认情况如图所示:

Cell的分割线
less 复制代码
 // 设置分割线端距,这里表示separator离左边和右边均80像素
 myTableView.separatorInset = UIEdgeInsets(top: 0, left: 40, bottom: 0, right: 40)
 myTableView.separatorStyle = .singleLine    // 分割线的样式
 myTableView.separatorColor = .red   // 分割线颜色
隐藏部分Cell的分割线

因为TableView的风格设置是针对整个tableView的,如果设置myTableView.separatorStyle = .singleLine,则所有的cell都会有分割线,如果要隐藏部分cell的话就需要一点点小trick

在要显示cell的时候将cell的分割线inset左右各设为cell宽度的一半

swift 复制代码
 func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
     if indexPath.section == 1 {
         cell.separatorInset = .init(top: 0, left: cell.bounds.width / 2, bottom: 0, right: cell.bounds.width / 2)
     }
 }
Cell选中样式
ini 复制代码
 // cell的选中样式
 cell.selectionStyle = .default  // 默认点击时为灰色
 cell.selectionStyle = .none     // 设置为none点击时无反应 
 // 这里要注意一下,其实还有.grey和.blue但效果和.default是相同的
 // 在iOS7的时候更改了,然后如果想要设置为其他颜色,则要设置selectedBackgroundView
 // cell的选中样式为其他颜色
 cell.selectionStyle = .default
 cell.selectedBackgroundView = UIView()
 cell.selectedBackgroundView?.backgroundColor = .blue

UITableView的使用:

  • 在controller中引入UITableView类型的变量,然后设置相应的数据源和代理:

    ini 复制代码
     tableView.delegate = self
     tableView.datasource = self
  • 表格分组section的DataSource方法的调用时序:

DataSource数据源:需要在Controller引入UITableViewDataSource协议

  • 返回表格视图的Section数:numberOfSections

    swift 复制代码
     func numberOfSections(in tableView: UITableView) -> Int {
         // 返回section的数量
         return 1
     }
  • 设置 section中的row行的个数:

    swift 复制代码
     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         // 返回Section中的row行数
         return 1
     }
  • 返回Section的头部View:

    swift 复制代码
     func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
         // 返回对应Section的头部
         return UIView()
     }
  • 返回Section的尾部View:

    swift 复制代码
     func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
         // 返回对应Section的尾部
         return UIView()
     }

UITableViewDelegate:需要在Controller引入UITableViewDelegate协议

  • 返回每个cell对应的行高:

    swift 复制代码
     func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
         if indexPath.section == 0 {
             // section0 的cell行高为70
             return 70
         } else if indexPath.section == 1 {
             // section1 的cell行高为140
             return 140
         }
         // 默认行高
         return 106
     }
  • 返回Section头部的高度:

    swift 复制代码
     func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
             if section == 0 {
                 return 0.01
             }
             if (isShowUrlCell && section == 5) || (!isShowUrlCell && section == 4) {
                 return 0.01
             }
             return 8
         }
  • 返回Section尾部的高度:

    swift 复制代码
     func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
         return 0.01
     }
  • Cell的点击事件:

    swift 复制代码
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
         if indexPath.section == 1 {
             // 针对section1操作
         } else if indexPath.section == 2 {
             // 针对Section2操作
         } else {
             // 默认操作
         }
     }

UITableView的一些常规函数/功能使用

  • 更新Cell,重新加载所有Cell

    • 此方法可重新加载用于构建表格的所有数据,包括单元格、节页眉和页脚、索引数组等。为了提高效率,表格视图只重新显示那些可见的行。如果表因重新加载而缩小,它会调整偏移量。表视图的代理或数据源在它希望表视图完全重新加载其数据时调用此方法。不应在插入或删除行的方法中调用它,尤其是在通过调用和实现的动画块中

      scss 复制代码
       tableView.reloadData()
  • Cell的左滑右滑

    swift 复制代码
     // 从左向右滑动
     func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
         let config = UISwipeActionsConfiguration(actions: [UIContextualAction(style: .normal, title: "点赞", handler: { _,_,_ in print("like")
         })])
         return config
     }
     // 从右向左滑动
     func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
         let config = UISwipeActionsConfiguration(actions: [UIContextualAction(style: .destructive, title: "删除", handler: {_,_,_ in print("delete")})])
         return config
     }
  • iOS中会对scrollview进行显示优化,以避免在安全区域显示,这里给出如何设置不自动调整位置

    ini 复制代码
     // 设置为.never即可让tableview的显示覆盖安全区域
     tableView.contentInsetAdjustmentBehavior = .never

TableView的性能优化

  1. UITableView 性能优化 - 掘金
  2. tableView性能优化 - 掘金
相关推荐
幸福回头20 小时前
ms-swift 代码推理数据集
llm·swift
若水无华1 天前
fiddler 配置ios手机代理调试
ios·智能手机·fiddler
不二狗1 天前
每日算法 -【Swift 算法】Two Sum 问题:从暴力解法到最优解法的演进
开发语言·算法·swift
Aress"1 天前
【ios越狱包安装失败?uniapp导出ipa文件如何安装到苹果手机】苹果IOS直接安装IPA文件
ios·uni-app·ipa安装
Jouzzy2 天前
【iOS安全】Dopamine越狱 iPhone X iOS 16.6 (20G75) | 解决Jailbreak failed with error
安全·ios·iphone
瓜子三百克2 天前
采用sherpa-onnx 实现 ios语音唤起的调研
macos·ios·cocoa
左钦杨2 天前
IOS CSS3 right transformX 动画卡顿 回弹
前端·ios·css3
努力成为包租婆2 天前
SDK does not contain ‘libarclite‘ at the path
ios
安和昂3 天前
【iOS】Tagged Pointer
macos·ios·cocoa
I烟雨云渊T3 天前
iOS 阅后即焚功能的实现
macos·ios·cocoa