Swift - 表格UITableView的plain、grouped两种样式详解(附分组头悬停)
在表格 tableview 初始化的时候我们可以指定需要使用的 UITableViewStyle 样式,可用的样式一共有两种:.plain 和 .grouped。下面分别对它们做介绍。
一、plain模式
1,默认样式
- 在 plain 模式下,如果 tableview 有多个 section(分区、分组),组与组之间默认是没有间距的。
- 同时组头或组尾会有 sticky 效果(粘性效果、悬停效果),即表格滚动时组头与组尾会自动停留,而不是跟随单元格一同移动。
import UIKit class ViewController: UIViewController , UITableViewDelegate, UITableViewDataSource{ var tableView:UITableView? //分组头标题 var articleHeaders:[String]! //所有文章标题 var articleNames:Dictionary<Int, [String]>! override func loadView() { super.loadView() } override func viewDidLoad() { super.viewDidLoad() //初始化数据 self.articleNames = [ 0:[String]([ "1、文本标签(UILabel)的用法", "2、按钮(UIButton)的用法", "3、文本输入框(UITextField)的用法", "4、多行文本输入框(UITextView)的用法", "5、开关按钮(UISwitch)的用法", "6、分段选择控件(UISegmentedControl)的用法", "7、图像控件(UIImageView)的用法", ]), 1:[String]([ "1、使用占位符文本placeholder添加文本框提示", "2、使用autofocus让控件自动获取焦点", "3、表单客户端验证", "4、日期和时间选择输入", "5、颜色选择器",]) ] self.articleHeaders = [ "Swift文章", "HTML5文章" ] //创建表视图 self.tableView = UITableView(frame:self.view.frame, style:.plain) self.tableView!.delegate = self self.tableView!.dataSource = self //创建一个重用的单元格 self.tableView!.register(UITableViewCell.self, forCellReuseIdentifier: "SwiftCell") self.view.addSubview(self.tableView!) } //在本例中,有2个分区 func numberOfSections(in tableView: UITableView) -> Int { return self.articleHeaders.count } //返回表格行数(也就是返回控件数) func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let data = self.articleNames[section] return data!.count } // UITableViewDataSource协议中的方法,该方法的返回值决定指定分区的头部 func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return self.articleHeaders[section] } // UITableViewDataSource协议中的方法,该方法的返回值决定指定分区的尾部 func tableView(_ tableView:UITableView, titleForFooterInSection section:Int)->String? { let data = self.articleNames[section] return "有\(data!.count)篇文章" } //创建各单元显示内容(创建参数indexPath指定的单元) func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { //为了提供表格显示性能,已创建完成的单元需重复使用 let identify:String = "SwiftCell" //同一形式的单元格重复使用,在声明时已注册 let cell = tableView.dequeueReusableCell(withIdentifier: identify, for: indexPath) cell.accessoryType = .disclosureIndicator var data = self.articleNames[indexPath.section] cell.textLabel?.text = data![indexPath.row] return cell } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }
2,调整分组间的间距
如果需要设置组与组之间的间距,可以通过 viewForHeaderInSection 、viewForFooterInSection 、heightForHeaderInSection 或 heightForFooterInSection 这几个方法配合实现。3,去除分组头、分组尾的停留效果
这个通过重写 tableView 的 scrollViewDidScroll 方法可以实现。要注意的是页面是否有导航控制器,有的话要把自动内边距调整给考虑进去。
(1)分组头部不悬停
//header不悬停 func scrollViewDidScroll(_ scrollView: UIScrollView) { //组头高度 let sectionHeaderHeight:CGFloat = 30 //获取是否有默认调整的内边距 let defaultEdgeTop:CGFloat = navigationController?.navigationBar != nil && self.automaticallyAdjustsScrollViewInsets ? 64 : 0 if scrollView.contentOffset.y >= -defaultEdgeTop && scrollView.contentOffset.y <= sectionHeaderHeight - defaultEdgeTop { scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0) } else if (scrollView.contentOffset.y>=sectionHeaderHeight - defaultEdgeTop) { scrollView.contentInset = UIEdgeInsetsMake(-sectionHeaderHeight + defaultEdgeTop, 0, 0, 0) } }
(2)分组尾部不悬停
//footer不悬停 func scrollViewDidScroll(_ scrollView: UIScrollView) { //组尾高度 let sectionFooterHeight:CGFloat = 30 //获取是否有默认调整的内边距 let defaultEdgeTop:CGFloat = navigationController?.navigationBar != nil && self.automaticallyAdjustsScrollViewInsets ? 64 : 0 let b = scrollView.contentOffset.y + scrollView.frame.height let h = scrollView.contentSize.height - sectionFooterHeight if b <= h { scrollView.contentInset = UIEdgeInsetsMake(defaultEdgeTop, 0, -30, 0) }else if b > h && b < scrollView.contentSize.height { scrollView.contentInset = UIEdgeInsetsMake(defaultEdgeTop, 0, b - h - 30, 0) } }
(3)分组头部、尾部均不悬停
//header、footer均不悬停 func scrollViewDidScroll(_ scrollView: UIScrollView) { //组头高度 let sectionHeaderHeight:CGFloat = 30 //组尾高度 let sectionFooterHeight:CGFloat = 30 //获取是否有默认调整的内边距 let defaultEdgeTop:CGFloat = navigationController?.navigationBar != nil && self.automaticallyAdjustsScrollViewInsets ? 64 : 0 //上边距相关 var edgeTop = defaultEdgeTop if scrollView.contentOffset.y >= -defaultEdgeTop && scrollView.contentOffset.y <= sectionHeaderHeight - defaultEdgeTop { edgeTop = -scrollView.contentOffset.y } else if (scrollView.contentOffset.y>=sectionHeaderHeight - defaultEdgeTop) { edgeTop = -sectionHeaderHeight + defaultEdgeTop } //下边距相关 var edgeBottom:CGFloat = 0 let b = scrollView.contentOffset.y + scrollView.frame.height let h = scrollView.contentSize.height - sectionFooterHeight if b <= h { edgeBottom = -30 }else if b > h && b < scrollView.contentSize.height { edgeBottom = b - h - 30 } //设置内边距 scrollView.contentInset = UIEdgeInsetsMake(edgeTop, 0, edgeBottom, 0) }
二、grouped模式
1,默认样式
- 在 grouped 模式下,如果 tableview 有多个 section(分区、分组),组与组之间默认是有间距的。
- 而且在表格滚动的同时组头与组尾会随之滚动、不停留,不会有 sticky 效果(粘性效果、悬停效果)。
2,去掉多余的间距
(1)在分组头、分组尾都存在时,可以将 tableview 最上方的间距给去除。
(2)如果只有分组头,没有分组尾,除了将 tableview 最上方的间距给去除,还可以将分组尾的高度设置为 0.01(不能设为 0,否则无效)。同时还要将分组尾设置成一个空的 UIView(否则在 iOS11 下分组尾高度不会起作用)。
(3)如果分组头、分组尾均没有,还可以将分组头的高度设置为 0.01。
//去除表格上放多余的空隙 self.tableView?.contentInset = UIEdgeInsetsMake(-20, 0, 0, 0)
(2)如果只有分组头,没有分组尾,除了将 tableview 最上方的间距给去除,还可以将分组尾的高度设置为 0.01(不能设为 0,否则无效)。同时还要将分组尾设置成一个空的 UIView(否则在 iOS11 下分组尾高度不会起作用)。
//去除表格上放多余的空隙 self.tableView?.contentInset = UIEdgeInsetsMake(-20, 0, 0, 0) //设置分组尾的高度 func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { return 0.01 } //将分组尾设置为一个空的View func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { return UIView() }
(3)如果分组头、分组尾均没有,还可以将分组头的高度设置为 0.01。
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 0.01 }
航哥,我的应用场景是这样的。我只有一个分区,想个这个分区添加一个页眉,然后页眉固定位置,列表可以滚动,并且点击页眉可以到另一个页面,要怎么处理一下,我是小白,不太懂!
航哥,想请教一下,如像AppStore里的更新页哪样。我有CoreData数据,数据里存有Date(储存日期有同一天和不同一天的),想在TableViewHeader中显示日期,同一天储存的数据只在当天的分组中显示应怎做?
//获取是否有默认调整的内边距
let defaultEdgeTop:CGFloat = self.automaticallyAdjustsScrollViewInsets ? 64 : 0
航哥 还有一个问题请教 就是self.automaticallyAdjustsScrollViewInsets的默认值为true 你这样设置的话在你这个例子是可以的 但是如果在没有导航的情况下self.automaticallyAdjustsScrollViewInsets的值为默认值 这个判断也就会偏移了64
航哥但是有表头 怎么偏移20呢?
写的很好,航哥,请教一下:浏览一个数组的图片,一页一页的浏览,使用collection view,点击cell,图片就全屏显示,全屏显示的时候,图片可以放大缩小。我遇到的问题就是“点击cell,图片就全屏显示,全屏显示的时候,图片可以放大缩小。”这些我不知道如何实现