美文网首页君赏博客
关于 ZHTableViewGroup 的设计之路

关于 ZHTableViewGroup 的设计之路

作者: 君赏 | 来源:发表于2017-03-16 15:12 被阅读56次

    关于 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

    相关文章

      网友评论

      • MYS_iOS_8801:如果 有多个cell 但是没有cell对应的属性值是不一样的 你的 代码里面并没有 体现
        君赏:@MYS_iOS_8801 只要样式同一个都属于一个cell 不同可以参考demo

      本文标题:关于 ZHTableViewGroup 的设计之路

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