关于 ZHTableViewGroup 的设计之路

关于 ZHTableViewGroup 的设计之路

关于ZHTableViewGroup思想如何产生

  • 之前复杂的页面不用表格要对于小屏幕做适配添加滚动 需要可以滚动的试图无非就是 UIScrollView 或者 UIScrollView 的子类
  • 删除页面某些试图或者增加没那么容易
  • 做复杂的表单十分复杂要写一些判断逻辑 十分的复杂
  • 对于表格的元素赋值要精确不认会 crash
  • 等等其他吐槽的原因

我对于针对 UITableView 平时经常用到的方法和判断做一些分离 这样岂不是就可以分开逻辑 单独处理?

经过这样的思考,觉得这个办法还是可以的,ZHTableViewGroup应运而生

关于ZHTableViewGroup的架构

用户只要负责创建 ZHTableViewDataSource 对象

添加ZHTableViewGroup 分组->添加ZHTableViewCell 模块

比如下面的界面怎么做呢

这个界面可以分成下面的模块

分为三种不同的模块

中间的空格也可以作为一个单独的模块

我们对于 UITableViewDataSource和 UITableViewDelegate 的方法进行分离

返回组的个数

public func numberOfSections(in tableView: UITableView) -> Int //返回组的个数

这个代理方法是设置表格的分组个数 我们用 ZHTableViewGroup 分别代表表格的组

对于用户首先要创建一个ZHTableViewGroup 的数据源对象 ZHTableViewDataSource

因为我们需要知道表格的对象地址,所以我们初始化的时候传入 UITableView 的对象

    /// 初始化ZHTableViewDataSource数据源
    ///
    /// - Parameter tableView: 表格对象
    public init(tableView:UITableView) {
        self.tableView = tableView
        super.init()
    }

我们创建一个 UITableView 的变量来指向这个内存地址

    /// 托管 UITableView 的对象
    var tableView:UITableView

之前准备想让用户不用实现 UITableViewDataSource 的代理方法 用运行时或者代理卸载这个库里面 用最简单的代码来完成

最后分析了这样妨碍用户一些自定义的事情 决定还是让用户调用库的方法

我们创建一个数组用于存放 ZHTableViewGroup

    /// ZHTableViewGroup的数组
    public var groups:[ZHTableViewGroup] = []

因为 UITableView 执行代理的时候 可能用户的 ZHTableViewDataSource 对象还没有创建 所以我们要创建类方法去返回组的个数

    /// 返回分组的个数
    ///
    /// - Parameter dataSource: ZHTableViewDataSource数据源可以为 nil
    /// - Returns: Int分组的个数
    public class func numberOfSections(dataSource:ZHTableViewDataSource?) -> Int {
        guard let dataSource = dataSource else {
            // 当ZHTableViewDataSource用户对象还没有创建的时候返回0
            return 0
        }
        return dataSource.groups.count // 返回 ZHTableViewGroup 数组的个数
    }

关于groups数组的元素怎么来呢 ? 我们写一个方法来添加元素

    /// 添加分组
    ///
    /// - Parameter completionHandle: 添加分组配置的回调
    public func addGroup(completionHandle:ZHTableViewAddGroupCompletionHandle) {
        let group = ZHTableViewGroup()
        completionHandle(group)
        groups.append(group)
    }

为什么用回调呢 因为可以让用户可以在外部自定义配置 思想来源于 Masonry

/// 添加分组的回调 group:回调的ZHTableViewGroup
public typealias ZHTableViewAddGroupCompletionHandle = (_ group:ZHTableViewGroup) -> Void

返回每组 Cell 的总数

public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int // 返回每组 cell 的总数

我们创建一个类方法返回 cell的总数

    /// 返回每组 Cell 的总数
    ///
    /// - Parameters:
    ///   - dataSource: ZHTableViewDataSource数据源对象可以为 nil
    ///   - section: 组的索引
    /// - Returns:  cell的总数
    public class func numberOfRowsInSection(dataSource:ZHTableViewDataSource?, section:Int) -> Int {
        guard let group = groupForSection(dataSource: dataSource, section: section) else {
            // 如果获取不到对应的 ZHTableViewGroup 对象就返回0
            return 0
        }
        return group.cellCount
    }

获取 ZHTableViewGroup 的方法

    ///  获取对应的分组
    ///
    /// - Parameters:
    ///   - dataSource: ZHTableViewDataSource的数据源可以为 nil
    ///   - section: 分组的索引
    /// - Returns: 对应分组对象可能为 nil
    private class func groupForSection(dataSource:ZHTableViewDataSource?, section:Int) -> ZHTableViewGroup? {
        guard let dataSource = dataSource else {
            // 当用户还没有创建ZHTableViewDataSource对象返回 nil
            return nil
        }
        guard dataSource.groups.count > section else {
            // 当取值的索引超出了边界返回 nil
            return nil
        }
        return dataSource.groups[section]
    }

对于 ZHTableViewGroup的属性cellCount

var cellCount:Int {
        get {
            var count:Int = 0 // 初始化默认 Cell 的数量为0
            for cell in self.cells {
                // 便利 cells 数组的 ZHTableViewCell 的对象
                count += cell.cellNumber // 对ZHTableViewCell的 cell 数量进行累加
            }
            return count
        }
    }

返回 UITableViewCell 的对象

public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell // 返回 UITableViewCell 的对象

我们创建类方法返回 UITableViewCell

    /// 返回对应的UITableViewCell
    ///
    /// - Parameters:
    ///   - dataSource: ZHTableViewDataSource数据源可以为空
    ///   - indexPath: 获取所在的 IndexPath
    /// - Returns: UITableViewCell
    public class func cellForRowAt(dataSource:ZHTableViewDataSource?, indexPath:IndexPath) -> UITableViewCell {
        guard let group = groupForSection(dataSource: dataSource, section: indexPath.section) else {
            // 当分组不存在返回默认的UITableViewCell
            return UITableViewCell()
        }
        guard let cell = group.cellForTableView(tableView: dataSource?.tableView, atIndexPath: indexPath) else {
            // 当获取UITableViewCell 获取不到返回默认的UITableViewCell
            return UITableViewCell()
        }
        return cell
    }

我们在 ZHTableViewGroup 里面来获取对应的 UITableViewCell

    /// 获取对应的 UITableViewCell
    ///
    /// - Parameters:
    ///   - tableView: 对应的表格 可能为 nil
    ///   - indexPath: 对应的 IndexPath 索引
    /// - Returns: UITableViewCell可能为 nil
    func cellForTableView(tableView:UITableView?, atIndexPath indexPath:IndexPath) -> UITableViewCell? {
        guard let tableView = tableView else {
            // 当表格不存在返回 nil
            return nil
        }
        guard let tableViewCell = tableViewCellForIndexPath(indexPath: indexPath) else {
            // 如果索引获取不到对应的 ZHTableViewCell 就返回 nil
            return nil
        }
        guard let identifier = tableViewCell.identifier else {
            // 如果用户没有设置 Identifier 就返回 nil
            return nil
        }
        let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) // 获取重用的 Cell
        tableViewCell.configCell(cell: cell, indexPath: indexPath) // 配置 cell
        return cell
    }

获取索引对应的 ZHTableViewCell

    /// 根本索引获取对应的ZHTableViewCell
    ///
    /// - Parameter indexPath:  IndexPath 的索引
    /// - Returns: ZHTableViewCell可能为 nil
    func tableViewCellForIndexPath(indexPath:IndexPath) -> ZHTableViewCell? {
        guard indexPath.row < self.cellCount else {
            // 如果索引超出了总个数就返回 nil
            return nil
        }
        var count:Int = 0 // 设置 cell 总数初始值
        var tableViewCell:ZHTableViewCell? // 保存ZHTableViewCell变量
        for cell in self.cells {
            // 便利 cells 数组里面的ZHTableViewCell
            count += cell.cellNumber // 累加 cell 的数量
            if indexPath.row < count {
                // 当索引在当前ZHTableViewCell范围内 就返回ZHTableViewCell对象
                tableViewCell = cell
                break
            }
        }
        return tableViewCell
    }

设置 Cell 的高度

public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat // 设置 Cell 的高度
    /// 获取 cell 的高度
    ///
    /// - Parameters:
    ///   - dataSource: ZHTableViewDataSource数组源
    ///   - indexPath: 索引位置
    ///   - customHeightCompletionHandle: 自定义高度方法回调
    /// - Returns:  cell 的高度
    public class func heightForRowAt(dataSource:ZHTableViewDataSource?, indexPath:IndexPath, customHeightCompletionHandle:ZHTableViewDataSourceCustomHeightCompletionHandle?) -> CGFloat {
        guard let cell = cellForIndexPath(dataSource: dataSource, atIndexPath: indexPath) else {
            // 如果 ZHTableViewCell 不存在就直接返回0
            return 0
        }
        return heightWithCustomHandle(height: cell.height, customCompletionHandle: customHeightCompletionHandle)
    }

获取高度判断的方法

    ///  返回高度
    ///
    /// - Parameters:
    ///   - height: 固定的高度
    ///   - customCompletionHandle: 自定义高度回调
    /// - Returns: 高度
    private class func heightWithCustomHandle(height:CGFloat, customCompletionHandle:ZHTableViewDataSourceCustomHeightCompletionHandle?) -> CGFloat {
        if height == CGFloat(NSNotFound) {
            // 如果用户没有设置高度 就查看用户是否自定义高度方法
            guard let customCompletionHandle = customCompletionHandle else {
                // 如果用户自定义高度方法不存在 就返回0
                return 0
            }
            return customCompletionHandle() // 返回用户的自定义高度
        } else {
            return height // 返回用户提前设定的固定高度
        }
    }

点击 Cell

public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) //点击 Cell
    /// 点击 cell
    ///
    /// - Parameters:
    ///   - dataSource: ZHTableViewDataSource数据源
    ///   - indexPath: 索引位置
    public class func didSelectRowAt(dataSource:ZHTableViewDataSource?, indexPath:IndexPath) {
        guard let tableViewCell = cellForIndexPath(dataSource: dataSource, atIndexPath: indexPath) else {
            // 当找不到 ZHTableViewCell 不存在就直接返回
            return
        }
        let cell = cellForRowAt(dataSource: dataSource, indexPath: indexPath) // 获取点击的 cell
        tableViewCell.didSelectRowAt(cell: cell, indexPath: indexPath) // 告诉ZHTableViewCell 点击了 cell
    }

更对的信息请查看 ReadMe

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容

  • 概述在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似...
    liudhkk阅读 8,833评论 3 38
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,036评论 29 470
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,595评论 4 59
  • 与人交谈时,试想着站在对方的角度去思考问题,往往每个人都更倾向于表达自己的观点,以致于在别人讲话的时候,自己就在想...
    乐此不疲的猫阅读 230评论 0 0
  • 爱情温柔甜如蜜,但它是最难懂亦是最难掌控的。一个人的品行经得起爱情的考验、岁月的流逝,那他还有什么令人不放心的呢?...
    静者居记阅读 302评论 0 6