Cell 自动注册实现方案

236 阅读1分钟

引言

日常码UI时是不是经常遇到这些情况?

  • 项目 run 起来后发现崩溃了😱,查看控制台输出发现 cell 忘记注册了😂
  • 注册代码不知道放在哪里合适🤔,跟复用代码很分裂

想一下,在复用cell时自动注册,上面的问题是不是都解决了

话不多说直接上代码🕶️

UITableView实现

import UIKit

private var kCellRegisterTable: Void?
private var kHeaderFooterRegisterTable: Void?

// MARK: 注册复用
extension NameSpace where Base: UITableView {
    
    /// 免注册复用cell,未注册时会自动注册,并记录到cellRegisterTable
    public func dequeueCell<T: UITableViewCell>(_ className: T.Type, for indexPath: IndexPath) -> T {
        let identifier = String(describing: className)
        if !base.cellRegisterTable.contains(identifier) {
            register(cell: className)
        }
        guard let cell = base.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as? T else {
            assertionFailure("Couldn't find UITableViewCell for \(identifier)")
            return T(style: .default, reuseIdentifier: identifier)
        }
        return cell
    }
    
    /// 免注册复用header footer view,未注册时会自动注册,并记录到headerFooterRegisterTable
    public func dequeueHeaderFooter<T: UITableViewHeaderFooterView>(_ className: T.Type) -> T {
        let identifier = String(describing: className)
        if !base.headerFooterRegisterTable.contains(identifier) {
            register(headerFooter: className)
        }
        guard let headerFooterView = base.dequeueReusableHeaderFooterView(withIdentifier: identifier) as? T else {
            assertionFailure("Couldn't find UITableViewHeaderFooterView for \(identifier)")
            return T(reuseIdentifier: identifier)
        }
        return headerFooterView
    }
    
    /// 注册header footer view,记录到headerFooterRegisterTable
    public func register<T: UITableViewHeaderFooterView>(headerFooter className: T.Type) {
        let identifier = String(describing: className)
        base.headerFooterRegisterTable.insert(identifier)
        base.register(T.self, forHeaderFooterViewReuseIdentifier: identifier)
    }
    
    /// 注册cell,记录到cellRegisterTable
    public func register<T: UITableViewCell>(cell className: T.Type) {
        let identifier = String(describing: className)
        base.cellRegisterTable.insert(identifier)
        base.register(T.self, forCellReuseIdentifier: identifier)
    }
}

private extension UITableView {
    var cellRegisterTable: Set<String> {
        get {
            if let table = objc_getAssociatedObject(self, &kCellRegisterTable) as? Set<String> {
                return table
            } else {
                objc_setAssociatedObject(self, &kCellRegisterTable, Set<String>(), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                return []
            }
        }
        set {
            objc_setAssociatedObject(self, &kCellRegisterTable, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    var headerFooterRegisterTable: Set<String> {
        get {
            if let table = objc_getAssociatedObject(self, &kHeaderFooterRegisterTable) as? Set<String> {
                return table
            } else {
                objc_setAssociatedObject(self, &kHeaderFooterRegisterTable, Set<String>(), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                return []
            }
        }
        set {
            objc_setAssociatedObject(self, &kHeaderFooterRegisterTable, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

UICollectionView实现

自己动手试试吧