美文网首页Swift编程
Swift 优雅使用TableView ZJTableViewM

Swift 优雅使用TableView ZJTableViewM

作者: jzhang | 来源:发表于2020-04-16 17:12 被阅读0次

    关于ZJTableViewManager

    强大的数据驱动的TableView,构建复杂TableView从未如此轻松。

    适配

    Version Swift Xcode
    0.2.7 4.0 / 4.2 Xcode10 or later
    1.0.3 or later 4.0 / 4.2 / 5 Xcode10.2 or later

    简介

    ZJTableViewManager 基于数据驱动页面的理念,接管了UITableViewdelegatedataSource的逻辑,开发者只需要关心数据的处理,避免了冗长的判断,让代码更加易于维护。

    比如一个页面里面是UITableView,有5种不同的Cell。按照传统的写法,tableView(_:cellForRowAt:)代理方法里会是这样:

    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            if condition1 {
                return SimpleStringCell
            }else if condition2 {
                return FullLengthTextFieldCell
            }else if condition3 {
                return TextCell
            }else if condition4 {
                return PasswordCell
            }else if condition5 {
                return SwitchCell
            }else {
                return DefaultCell
            }
    }
    

    同时下面两个方法里面也很可能需要写上这一串判断条件。显而易见的缺点:代码冗长

    tableView(_:, heightForRowAt:)
    tableView(_:, didSelectRowAt:)
    

    其次,实际项目中,很多人直接用IndexPath作为判断条件,大量if else,且当需要对Cell显示顺序做调整时,基于IndexPath的判断就会出问题,改起来特别容易出现Bug。

    当然有经验的程序员会抽象出一个type,通过type来判断Cell类型,避免IndexPath的缺陷。这其实已经算是一种数据驱动的思想了,相比用IndexPath判断,更加不容易出问题。但是这还不够,这些方法里面依然会有很多if else,影响观感也影响逻辑理解。

    所以ZJTableViewManager 在以上的基础上进一步做了封装,效果如下:

    就如示例代码所示,不需要处理TableView的delegate和dataSource,不需要那些if else,Item控制Cell处理Cell的事件,我们只要专心用代码描述这个TableView长什么样子,它就会按照我们描述的样子搭建出来。

    使用方式

    1.系统默认Cell

    创建系统默认的cell,使用ZJTableViewItem类,创建之后加入section即可

    let item = ZJTableViewItem(title: "测试cell 1")
    section.add(item: item)
    

    根据需要可以修改样式为subtitle

    item.style = .subtitle
    item.detailLabelText = "detail label text"
    

    运行结果:


    总结一下系统默认Cell的使用步骤:

    1. 页面上创建一个TableView(StoryBoard拖或者纯代码创建都可以)
    2. 通过这个TableView初始化一个manager
    3. 创建一个Section,加入到manager里
    4. 创建Cell对应的Item,赋值之后加入到section里
    5. manager.reload()

    具体不展开说了,系统cell就那几个样式,平时也很少用到,自己尝试吧。

    2.自定义Cell

    自定义Cell才是我们实际项目中用到最多的,所以这一块需要详细说一下。
    我们来尝试自定义这样一个Cell

    左边是一个UILabel,右边一个UISwitch,功能是在UISwitch开关时会发出回调,在VC中处理。

    首先,新建一个ZJSwitchCell类,继承自UITableViewCell,勾选上Also create XIB file

    在xib文件里面拖上控件,并且把控件和UISwitch的value change事件拖线到Cell文件里面,

    下面是重点:
    在ZJSwitchCell.swift文件里面写一个ZJSwitchCellItem类,继承自ZJTableViewItem,有三个属性,标题title,开关状态isOn,回调闭包didChanged。

    让ZJSwitchCell遵循ZJCellProtocol协议,如图所示,Xcode会弹出提示,点击fix,会自动加上需要的方法和类型

    ZJCelltemClass这里填写上前面写好的ZJSwitchCellItem类名

    然后Xcode还会有个错误提示,继续点fix,就好了。


    可能有时候Xcode自动fix补全的代码有问题,比如说typealias ZJCelltemClass =出现两遍或者根本没有fix按钮,不要慌,cmd+b编译一下,再试试就好了

    然后在cellWillAppear()方法里面写上赋值操作,相当于我们在tableView(_:, cellForRowAt:)方法里面写的代码。再到valueChanged(:)方法里面,记录UISwitch的状态,并把当前这个item通过回调传出去。Cell部分的自定义就完成了。

    最后,在VC里面使用,使用之前需要manager.register(ZJSwitchCell.self, ZJSwitchItem.self)注册一下,这和之前使用系统默认的Cell有区别,自定义的Cell都需要注册一下才可以使用。

    class FormViewController: UIViewController {
        var tableView: UITableView!
        var manager: ZJTableViewManager!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            tableView = UITableView(frame: view.bounds, style: .plain)
            view.addSubview(tableView)
            manager = ZJTableViewManager(tableView: tableView)
            manager.register(ZJSwitchCell.self, ZJSwitchItem.self)
    
            let section = ZJTableViewSection()
            manager.add(section: section)
            
            // Switch Item
            section.add(item: ZJSwitchItem(title: "Switch Item", isOn: false, didChanged: { item in
                zj_log(item.isOn)
            }))
    
            manager.reload()
        }
    }
    

    总结一下自定义Cell的步骤:

    1. 新建Cell(XIB或者纯代码都可以)
    2. 创建Cell对应的Item,通过Item给Cell传值(实际项目中一般是用Item持有Model,在cellWillAppear()中通过item.model取值)
    3. 在VC中给把Cell向TableViewManager注册。

    其余使用方式参考上面系统默认Cell的使用。

    3.Cell固定高度及动态计算高度处理

    前面示例的两种Cell高度都是系统默认的44,我们实际项目中需要不同高度的Cell,怎么处理呢?

    固定高度:
    聪明的同学可能已经发现了,Item控制了Cell的所有表现,所以肯定是通过Item来控制的。Item中有个cellHeight属性,只要给它赋值,就能够控制Cell的高度。
    我们可以重写Item对象的init()方法,在里面给一个固定的高度

    override init() {
       super.init()
       cellHeight = 100
    }
    

    或者在VC里面初始化Item之后,再给cellHeight赋值,都是可以的。

    动态高度:
    动态高度的前提是使用AutoLayout布局,约束没有缺失,然后在Item赋值好了之后,调用一下autoHeight(:)方法,高度就算好了。

    let item = AutomaticHeightCellItem()
    item.feed = feed
    //计算高度
    item.autoHeight(manager)
    //把cell加入进section
    section.add(item: item)
    

    具体可以看下面的文章,里面说的更详细,这里不展开说了。
    Swift UITableViewCell高性能动态计算高度

    4.TableView相关事件(如点击事件)

    设置点击事件回调:

    item.setSelectionHandler { (callBackItem: LevelCellItem) in
        //Do some thing
    }
    

    其他事件同理,包括section的一些事件(比如section即将出现之类的回调),具体看Demo。

    5.Scroll事件的代理

    我们在使用TableView的同时,有时还需要处理Scroll事件,比如说判断滚动已经停止,或者监听滚动事件等,可以通过设置manager.scrollDelegate = self并遵循ZJTableViewScrollDelegate的方式,获取所有滚动事件的回调,使用方式和UIScrollViewDelegate一样

    使用

    直接拖入ZJTableViewManager文件夹里面的文件,或者用cocoapods
    pod 'ZJTableViewManager'

    项目地址:ZJTableViewManager

    相关文章

      网友评论

        本文标题:Swift 优雅使用TableView ZJTableViewM

        本文链接:https://www.haomeiwen.com/subject/quxlvhtx.html