【UIKit】UITableView

官方文档:Table View Programming Guide for iOS

UITableView 适用于单表格展示,支持滑动。

UITableViewDelegate协议

    官网文档:UITableViewDelegate

UITableviewDelegate作用:对UITableview的整个生命周期的各过程或者各种事件状态设置接口,方便你在某种时刻或者事件状态下做自定义操作。具体分为:

1.某个视图模块(cell、header、footer)的将要显示结束显示.

2.某个视图在某个位置index/section的高度/预留高度。

3.传图UIView作为header、footer的视图。

4.附属按钮点击时候

5.高亮状态的三个状态。

6.选择取消选择的两个状态(will和didEnd)

7.编辑状态编辑类型设置(没有、删除、插入)、自定义删除按钮的字【titleForDelecteXXXX】、自定义增加多个按钮【editActionForRow】,注意自带的删除将会消失,所以可以自己加上,最先放入的在右边、是否首行缩进(indent)??

8.移动时的操作接口

9.复制与粘贴菜单操作(如果需要的实现相关三个代理【performAction】)

10.与焦点(focus)有关的几个接口【不知道干嘛用的】

Tips: 如果TableViewCell满足约束,设置tableView.rowHeight = UITableView.automaticDimension 即可实现自动撑开 Cell。

[官网文档:Self-Sizing Table View Cells]

UITableViewDataSource协议

官网文档:UITableViewDataSource

1.确定表格模块是,sectionNumber。

2.确定每个块中有多少行。

3.每行的数据填充。

4.header与footer的text填充,【疑问:字体颜色样式怎么控制?自定义header覆盖吗?】。

5.编辑(插入、移动、删除的数据源处理)

6.添加右侧栏的侧栏字母序列【注意:tableView的style必须为plain】

7.右侧索引字母对应的某个模块,相当于将右侧索引字母与模块关联起来。

扩展:右侧索引字母的颜色与tableView的tintColor一致。

UITableViewDataSourcePrefetch协议

主要就两个方法,一个预加载数据(比如在其中进行数据源的预加载处理),另一个取消预加载,可以提高很大性能,大量数据建议使用。

对表格进行增删

1、确定删除位置

2、先更新数据源【特重要,不然会发生错误终止,因为行数不对应/不够,】

3、删除或插入行

4、更新表格

示例:评分cell的显示与隐藏

    /**
     *  选择评分
     */
    func tableViewSelectScore(){
        self.tableview?.beginUpdates() //开始更新表格(一般是用于插入或删除行)
        let tempIndexPath = [NSIndexPath(forRow: 2, inSection: 0)] //首先确定哪一个路径改变(某一段的某行(基数是从0开始的)))
        if showScore{//如果已经点开拉评论
            //说明评论已经打开,需要取消掉评论行
            //1.取消前需要处理数据源,行数减少一个
            self.titleArray.removeAtIndex(2)
            //2移除那行
            self.tableview?.deleteRowsAtIndexPaths(tempIndexPath, withRowAnimation: UITableViewRowAnimation.Right)//从右边划出
            self.showScore = false  //如果是有一个可选显示的,可以设一个标记变量
        }else{
        self.titleArray.insert("", atIndex: 2)   //很重要,在表格对应的数据源添加数据,不然就会发生表格行数不够的错误,引起程序终止
        self.tableview?.insertRowsAtIndexPaths(tempIndexPath, withRowAnimation: UITableViewRowAnimation.Left) //插入位置是【参数是数组】,及动画效果(从左到右)
        self.showScore = true  //显示星星
        }
        self.tableview?.endUpdates() //结束更新
    }

多个增删,建议使用performBatchUpdates(_:completion:) | Apple Developer Documentation

补充提示:

1、表格视图的Footer 与header,注意与sectione的footer和header不是同一个

UITableViewCell

通过子类化 UITableViewCell来自定义 Cell, 添加需要的控件。

通常建议将需要展示的内容先添加在自定义 View上,再将 UITableViewCell 作为容器显示(理由参考:Why I never subclass UITableViewCell or UICollectionViewCell)。 优点:可以复用 View(用在其他 View、UITableViewCell、UICollectionCell 等各种组合)
 定义显示 View

// 定义显示 View

class ProductView: UIView {
    // all the same properties
    // ...

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupLayout()
    }

    private func setupLayout() {
      // all the same layout
      // ...
    }

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

    func configure(with product: ProductViewModel) {
      // same configuration logic
      // ...
    }
}

更好的兼容 SwiftUI

import SwiftUI

struct ProductViewRepresentable: UIViewRepresentable {
    let product: ProductViewModel

    func makeUIView(context: Context) -> ProductView {
        return ProductView()
    }

    func updateUIView(_ uiView: ProductView, context: Context) {
        uiView.configure(with: product)
    }
}

定义Swift 泛型。

public class ViewEmbeddingTableViewCell<EmbeddedView: UIView>: UITableViewCell {
    public let embeddedView: UIView = {
        let view = EmbeddedView(frame: .zero)
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)

        contentView.addSubview(embeddedView)
        // 这里还可以定义统一边距,以便统一控制 Cell
        NSLayoutConstraint.activate([
            embeddedView.topAnchor.constraint(equalTo: contentView.topAnchor),
            embeddedView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            embeddedView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
            embeddedView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
        ])
    }

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

完美使用

tableView.register(
  ViewEmbeddingTableViewCell<ProductView>.self,
  forCellWithReuseIdentifier: "ProductCell"
)

let cell = collectionView.dequeueReusableCell(
    withReuseIdentifier: "ProductCell",
    for: indexPath
) as? ViewEmbeddingCollectionViewCell<ProductView>

cell?.embeddedView.configure(with: productViewModel)

自定义header/Footer​​​​​​​

1、 还需要添加一些UI控件或定制样式,这就需要通过tableview的代理方法viewForHeaderInSection返回值为UIView)内进行定制

2、 定制思想:创建需要的控件对象,配置好控件对象,创建一个视图对象,将控件对象添加到视图对象并布局好即可,做好相应的响应处理(必要时)

refreshControl

如果直接使用UITableViewController,是可以自带刷新控件的,默认是不使用,需要则设置true, 但是只可以下拉刷新,

使用的几个注意点,

1.下拉后自动执行

     // self.refreshControl?.beginRefreshing() //!!!:

 会立即自动调用tableView.ReloadDatatableView cellForRow xxx的代理方法,给cell填充的方法,但是不会执行

func numberOfSections(in tableView:UITableView) ->Int,

所以需要注意:此时数据源最好不变,否则可能会导致越界异常

2.需要手动在一定的逻辑处结束刷新。

self.refreshControl?.endRefreshing()  //之后显示重新加载table

默认的footer(如果不设置为0.001f的话)【此图片用于评论区】

UITableView顶部留白问题

UITableView(Plain)取消footer和header的粘黏

示例代码

func scrollViewDidScroll(_ scrollView: UIScrollView) {
        /// 取消粘连
        let sectionHeaderHeight:CGFloat = 30;
        let sectionFooterHeight: CGFloat = 55
        let offsetY = scrollView.contentOffset.y
        if (offsetY <= sectionHeaderHeight && offsetY >= 0) {
            scrollView.contentInset = UIEdgeInsets.init(top: -offsetY, left: 0, bottom: -sectionFooterHeight, right: 0)
        } else if (offsetY >= sectionHeaderHeight) {
            scrollView.contentInset = UIEdgeInsets.init(top: -offsetY, left: 0, bottom: -sectionFooterHeight, right: 0)
        }else if (offsetY >= scrollView.contentSize.height - scrollView.frame.size.height - sectionFooterHeight && offsetY <= scrollView.contentSize.height - scrollView.frame.size.height){
            scrollView.contentInset = UIEdgeInsets(top: -offsetY, left: 0, bottom: (scrollView.contentSize.height-scrollView.frame.height-sectionFooterHeight), right: 0)
        }
    
    }

使用 Group 注意事项

如果我们不想保留 tabelView 的 header和 footer沾粘效果。可以利用 group 样式来取消,但是同时需要注意设置 headerView 和 footerView 的高度。

设置 tableViewFooterHeader

let header = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: CGFloat.leastNormalMagnitude))
let footer = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: CGFloat.leastNormalMagnitude))

tableView.tableViewHeaderView = header
tableView.tableViewFooterView = footer

设置 HeaderView 高度

以下方式视情况二选一,优先级: 代理方法>直接设置

直接设置

tableView.sectionHeaderHeight = CGFloat.leastNormalMagnitude

代理方式设置

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return CGFloat.leastNonzeroMagnitude // 或者0.01
}

设置 FooterView 高度

以下方式视情况二选一,优先级: 代理方法>直接设置

直接设置方式

tableView.sectionFooterHeight =  CGFloat.leastNormalMagnitude

代理方法设置

func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
    return CGFloat.leastNonzeroMagnitude //或者0.001, 注意0.01(或者CGFloat.leastNormalMagnitude)在 iOS14中还是有35空白
}

以上高度大部分可以直接设置0.01(footerView代理中需要使用0.001/CGFloat.leastNonzeroMagnitude),但是建议设置 `CGFloat.leastNormalMagnitude`,原因在于Cell滑动工程中可能会有白影闪过

最小 Float 正数值

Objective-C:  CGFLOAT_MIN

Swift:

CGFloat.leastNormalMagnitude :最小的正规则数

CGFloat.leastNonzeroMagnitude:最小正数(如果支持 subnormal,会比 CGFloat.leastNormalMagnitude 小,否则就是相等的)

关于 subnormal value 也叫 denormal number,统一称作非规格化浮点数​​​​​​​

修改 Header Title 样式

在多组标题时,代理方法只能传入 title, 那要如何修改样式了(在不重写 headerView前提下)?

只需要实现 代理方法的 willDisplayHeaderView 方法, 然后获取相应对象,修改即可。

 func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
        guard let header = view as? UITableViewHeaderFooterView else {
            return
        }
        header.textLabel?.font = UIFont.systemFont(ofSize: 12, weight: .medium)
        header.textLabel?.textColor = UIColor(hexString: "#787878")
        header.contentView.backgroundColor = UIColor(hexString: "#F8F8F8") // 注意直接修改 backgroundColor 没有效果。一般为nil 
    }

 section Header 取消背景默认灰色(不重写 headerView方式)

guard let header = view as? UITableViewHeaderFooterView else {
    return
}

//header.tintColor = .clear  // 可以实现,但是不推荐,可能影响其他控件
if #available(iOS 14.0, *) {
    header.backgroundConfiguration = UIBackgroundConfiguration.clear() 
} else {
    header.backgroundColor = .clear
}

兼容原因:在 iOS14后,增加了现代化配置 Cell ​​​​​​​功能,会在 background配置某些view.可参考 iOS14的 Modern cell configration给我们带来什么

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值