重用 1.0.0

重用 1.0.0

Oren Farhan维护。



重用 1.0.0

重用

Version License Platform

关于重用

重用是在尝试避免以简单统一的方式填充UITableViews/UICollectionViews的过程中创建的,以避免繁琐、重复的工作。

它使用InstanceReuser的概念,一个配置器来管理所有类似项目。一旦创建,所有数据都将由InstanceReuser提供以驱动UI。它还将处理交互和数据库更新。

示例

假设我们需要显示一个人员列表。让我们遵循最常用的模式。

首先,我们创建一些结构来保存数据

class PeopleDatabase {
    
    var people: [Person]
    
    init(people: [Person]) {
        self.people = people
    }
}

其次,是我们的视图控制器

class PeopleViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    private let cellIdentifier: String = "person.cell.id"

    @IBOutlet weak var tableView: UITableView!

    var database: PeopleDatabase!

    func viewDidLoad() {
        super.viewDidLoad()

        tableView.delegate = self
        tableView.dataSource = self
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return database.people.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let person = database.people[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! PersonCell
        cell.nameLabel.text = person.name
        cell.emailLabel.text = person.email
        // ...
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 120.0
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let person = database.people[indexPath.row]
        let viewController = PersonViewController(person: person)
        navigationController?.push(viewController, animated: true)
        tableView.deselectRow(at: indexPath, animated: true)
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        guard editingStyle == .delete else { return }
        // delete entry from database
        // update table
    }
    
    func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
        return true
    }
    
    func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        // update database
    }

}

当您只有一个表格时,这是可以的,但当您有更多屏幕时,它不可扩展并且将需要一些代码重复。

个人笔记

对我来说,我发现我在同一项目中有时缺乏结构的一致性。这意味着每次我回顾代码时都有一个学习过程。

这就是Reuse要做的事情。拥有一个可重用组件系统,它可以应用于实例,并且可以在整个应用中共享。

让我们修改项目以使用Reuse

首先,我们实现数据库协议

extension PeopleDatabase: Section, DataProvider {
    
    // MARK: DataProvider protocol

    var objects: [Usable] {
        get { return people }
        set { people = newValue }
    }
    
    func canDeleteObject(at index: ObjectIndex) -> Bool {
        return true
    }
    
    func canMoveObject(at index: ObjectIndex) -> Bool {
        return true
    }
}

通过使我们的数据库同时成为DataProviderSection,我们基本上可以说它只有一个部分。

然后我们创建一个可重用实例来配置我们的单元格。

struct PersonReuser: InstanceReuser {
    
    var viewIdentifier: String { return "person.cell.id" }
    var height: CGFloat { return 120.0 }
    
    private var person: Person?
    private weak var navigationController: UINavigationController?
    
    // We inject a navigation controller to handle our navigation.
    // I would prefer injecting some `Navigator` protocol, which will allow easier testing and abstraction, but for simplicity we'll just use this.
    init(navigationController: UINavigationController?) {
        self.navigationController = navigationController
    }
    
    // MARK: InstanceReuser protocol

    mutating func setObject(_ object: Usable) {
        person = object as? Person
    }
 
    func configure(_ reusable: Reusable) -> Bool {
        guard let person = person, let cell = reusable as? PersonCell else { return false }        
        cell.nameLabel?.text = person.name
        cell.emailLabel?.text = person.email
        return true
    }
    
    func select() {
        guard let person = person else { return }
        let viewController = PersonViewController(person: person)
        navigationController?.push(viewController, animated: true)
    }
}

现在只剩下一个重构我们的视图控制器以创建一个Reuser,并让它为我们处理所有事情。

class PeopleViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!

    var database: PeopleDatabase!
    private var reuser: Reuser!

    func viewDidLoad() {
        super.viewDidLoad()
        setupReuser()
        tableView.passHandling(to: reuser)
    }

    private func setupReuser() {
        reuser = Reuser(dataProvider: database)
        let instanceReuser = PersonReuser(navigationController: navigationController)
        reuser.register(instanceReuser, forObject: Person.self)
    }
}

这样您就完成了。Reuse将处理其他所有事情。您可以为PersonReuser添加逻辑,或者完全避免编写它,直接使用提供的通用一个。如果您需要更多控制,您可以实现UITableViewDataSourceUITableViewDelegate函数,并且只需使用索引访问您的重用。例如:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    reuser[indexPath].select()
    tableView.deselectRow(at: indexPath, animated: true)
}

现在,如果您需要在其他地方使用它,只需重用即可。无需代码重复。易于维护、更新或替换。

您可以做的还有更多,但我会让您去探索。

希望它对您的帮助和我的一样大。

安装

重用通过CocoaPods提供。要安装,只需将以下行添加到您的Podfile

pod 'Reuse'

作者

Oren Farhan

许可证

重用可遵守MIT许可证。有关更多信息,请参阅LICENSE文件。