-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathCellViewModel.swift
132 lines (102 loc) · 4.05 KB
/
CellViewModel.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//
// Created by Jesse Squires
// https://www.jessesquires.com
//
// Documentation
// https://jessesquires.github.io/ReactiveCollectionsKit
//
// GitHub
// https://github.com/jessesquires/ReactiveCollectionsKit
//
// Copyright © 2019-present Jesse Squires
//
import Foundation
import UIKit
/// Defines a view model that describes and configures a cell in the collection view.
public protocol CellViewModel: DiffableViewModel, ViewRegistrationProvider {
/// The type of cell that this view model represents and configures.
associatedtype CellType: UICollectionViewCell
var shouldHighlight: Bool { get }
var contextMenuConfiguration: UIContextMenuConfiguration? { get }
func configure(cell: CellType)
func didSelect(with controller: UIViewController)
}
extension CellViewModel {
public var shouldHighlight: Bool { true }
public var contextMenuConfiguration: UIContextMenuConfiguration? { nil }
public func didSelect(with controller: UIViewController) { }
}
extension CellViewModel {
/// The cell class for this view model.
public var cellClass: AnyClass { CellType.self }
/// A default reuse identifier for cell registration.
/// Value defaults to the name of the class implementing the `CellViewModel` protocol.
public var reuseIdentifier: String { "\(Self.self)" }
/// A default registration for this view model for class-based cells.
/// - Warning: Does not work for nib-based cells.
public var registration: ViewRegistration {
ViewRegistration(
reuseIdentifier: self.reuseIdentifier,
cellClass: self.cellClass
)
}
/// Returns a type-erased version of this view model.
public var anyViewModel: AnyCellViewModel {
AnyCellViewModel(self)
}
public func dequeueAndConfigureCellFor(collectionView: UICollectionView, at indexPath: IndexPath) -> CellType {
let cell = self.registration.dequeueViewFor(collectionView: collectionView, at: indexPath) as! CellType
self.configure(cell: cell)
return cell
}
}
/// A type-erased cell view model.
public struct AnyCellViewModel: CellViewModel {
// MARK: DiffableViewModel
public var id: UniqueIdentifier { self._id }
// MARK: ViewRegistrationProvider
public var registration: ViewRegistration { self._registration }
// MARK: CellViewModel
public typealias CellType = UICollectionViewCell
public var shouldHighlight: Bool { self._shouldHighlight }
public var contextMenuConfiguration: UIContextMenuConfiguration? { self._contextMenuConfiguration }
public func configure(cell: UICollectionViewCell) {
self._configure(cell)
}
public func didSelect(with controller: UIViewController) {
self._didSelect(controller)
}
// MARK: Private
private let _viewModel: AnyHashable
private let _id: UniqueIdentifier
private let _registration: ViewRegistration
private let _shouldHighlight: Bool
private let _contextMenuConfiguration: UIContextMenuConfiguration?
private let _configure: (CellType) -> Void
private let _didSelect: (UIViewController) -> Void
// MARK: Init
public init<T: CellViewModel>(_ viewModel: T) {
self._viewModel = viewModel
self._id = viewModel.id
self._registration = viewModel.registration
self._shouldHighlight = viewModel.shouldHighlight
self._contextMenuConfiguration = viewModel.contextMenuConfiguration
self._configure = { cell in
precondition(cell is T.CellType, "Cell must be of type \(T.CellType.self). Found \(cell.self)")
viewModel.configure(cell: cell as! T.CellType)
}
self._didSelect = { controller in
viewModel.didSelect(with: controller)
}
}
}
extension AnyCellViewModel: Equatable {
public static func == (left: AnyCellViewModel, right: AnyCellViewModel) -> Bool {
left._viewModel == right._viewModel
}
}
extension AnyCellViewModel: Hashable {
public func hash(into hasher: inout Hasher) {
self._viewModel.hash(into: &hasher)
}
}