Skip to content

Commit f65e0c4

Browse files
committed
Generalization of nodes concept and UITableView
1 parent 275d5f4 commit f65e0c4

File tree

11 files changed

+416
-291
lines changed

11 files changed

+416
-291
lines changed

Demos/Demos/TableViewController.swift

Lines changed: 112 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -71,106 +71,10 @@ extension TweetModel {
7171

7272
}
7373

74-
//class TableViewController: UIViewController {
75-
//
76-
// fileprivate lazy var tableView: UITableView = {
77-
// let tableView = UITableView(frame: CGRect(), style: .plain)
78-
//
79-
// tableView.delegate = self
80-
// tableView.dataSource = self
81-
// tableView.separatorStyle = .none
82-
// return tableView
83-
// }()
84-
//
85-
// fileprivate var nodeModels: [(TweetModel, RootNode)] = []
86-
// private var referenceWidth: CGFloat = 0
87-
//
88-
// override func viewDidLoad() {
89-
// super.viewDidLoad()
90-
//
91-
// title = "Table Demo"
92-
// view.addSubview(tableView)
93-
//
94-
// nodeModels = TweetModel.stubData().map { ($0, RootNode())}
95-
// }
96-
//
97-
// override func viewDidLayoutSubviews() {
98-
// super.viewDidLayoutSubviews()
99-
// tableView.lx.fill()
100-
//
101-
// let width = tableView.frame.width
102-
//
103-
// if width != referenceWidth {
104-
// referenceWidth = width
105-
//
106-
// let dateStart = Date()
107-
// let models = nodeModels.map { $0.0 }
108-
// DispatchQueue.global(qos: .background).async {
109-
// models.parallelMap(striding: 2, filler: (TweetModel(), RootNode()), f: {
110-
// ($0, TweetCell.buildRootNode($0, width: width))
111-
// }) {[weak self] in
112-
// print("did cache in \(Date().timeIntervalSince(dateStart))s")
113-
// self?.didLoad(nodeModels: $0, width: width)
114-
// }
115-
// }
116-
// }
117-
// }
118-
//
119-
// private func didLoad(nodeModels: [(TweetModel, RootNode)], width: CGFloat) {
120-
//
121-
// if width == referenceWidth {
122-
// self.nodeModels = nodeModels
123-
// }
124-
// }
125-
//}
126-
//
127-
//extension TableViewController: UITableViewDelegate, UITableViewDataSource {
128-
//
129-
// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
130-
// return nodeModels.count
131-
// }
132-
//
133-
// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
134-
// let cellId = "CellId"
135-
// let cell = (tableView.dequeueReusableCell(withIdentifier: cellId) as? TweetCell) ?? TweetCell(style: .default, reuseIdentifier: cellId)
136-
// return cell
137-
// }
138-
//
139-
// func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
140-
// tableView.deselectRow(at: indexPath, animated: true)
141-
// }
142-
//
143-
// func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
144-
// (cell as? TweetCell)?.rootNode = nodeModels[indexPath.row].1
145-
// }
146-
//
147-
// func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
148-
// return 100
149-
// }
150-
//
151-
// func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
152-
//
153-
// let (model, rootNode) = nodeModels[indexPath.row]
154-
//
155-
// if rootNode.frame.width != tableView.frame.width {
156-
// let newRootNode = TweetCell.buildRootNode(model, width: tableView.frame.width)
157-
// nodeModels[indexPath.row] = (model, newRootNode)
158-
//
159-
// if let cell = tableView.cellForRow(at: indexPath) {
160-
// if let cell = cell as? TweetCell {
161-
// cell.rootNode = newRootNode
162-
// }
163-
// }
164-
// }
165-
//
166-
// return nodeModels[indexPath.row].1.frame.height
167-
// }
168-
//}
169-
17074
class TableViewController: UIViewController {
17175

17276
fileprivate lazy var tableView: UITableView = {
173-
let tableView = UITableView(frame: CGRect(), style: .plain)
77+
let tableView = UITableView(frame: CGRect(), style: .grouped)
17478

17579
tableView.delegate = self
17680
tableView.dataSource = self
@@ -179,6 +83,7 @@ class TableViewController: UIViewController {
17983
}()
18084

18185
fileprivate var tweets: [TweetModel] = TweetModel.stubData()
86+
fileprivate var nodeModels: [(TweetModel, RootNode)]?
18287

18388
private var referenceWidth: CGFloat = 0
18489

@@ -187,52 +92,104 @@ class TableViewController: UIViewController {
18792

18893
title = "Table Demo"
18994
view.addSubview(tableView)
95+
96+
navigationItem.rightBarButtonItem = editButtonItem
97+
98+
nodeModels = tweets.map { ($0, RootNode())}
99+
}
100+
101+
override func setEditing(_ editing: Bool, animated: Bool) {
102+
super.setEditing(editing, animated: animated)
103+
tableView.setEditing(editing, animated: animated)
190104
}
191105

192106
override func viewDidLayoutSubviews() {
193107
super.viewDidLayoutSubviews()
108+
109+
110+
let width = view.frame.width
111+
112+
if width != referenceWidth {
113+
nodeModels = nil
114+
referenceWidth = width
115+
116+
let dateStart = Date()
117+
let models = tweets
118+
DispatchQueue.global(qos: .background).async {
119+
models.parallelMap(striding: 2, filler: (TweetModel(), RootNode()), f: {
120+
let node = TweetCell.buildRootNode($0, estimated: false)
121+
_ = node.calculate(for: CGSize(width: width, height: 0))
122+
return ($0, node)
123+
}) {[weak self] in
124+
print("did cache in \(Date().timeIntervalSince(dateStart))s")
125+
self?.didLoad(nodeModels: $0, width: width)
126+
}
127+
}
128+
}
129+
194130
tableView.lx.fill()
195131
}
196132

197-
fileprivate lazy var adapter: TableViewNodesDisplayAdapter = { [unowned self] in
198-
return TableViewNodesDisplayAdapter(headerNodeForSection: { index, estimated in
199-
return TweetCell.headerRootNode(title: "Cras justo odio, dapibus ac facilisis in, egestas eget quam. Lorem ipsum dolor sit amet, consectetur adipiscing elit.", estimated: estimated)
133+
private func didLoad(nodeModels: [(TweetModel, RootNode)], width: CGFloat) {
134+
135+
if width == referenceWidth {
136+
self.nodeModels = nodeModels
137+
}
138+
}
139+
140+
fileprivate lazy var adapter: TableViewPresentationAdapter = { [unowned self] in
141+
return TableViewPresentationAdapter(headerNodeForSection: { index, estimated in
142+
return NodeTableHeaderFooter(model: TweetCell.headerRootNode(title: "Cras justo odio, dapibus ac facilisis in, egestas eget quam. Lorem ipsum dolor sit amet, consectetur adipiscing elit.", estimated: estimated))
200143
}, cellNodeForIndexPath: { indexPath, estimated in
201-
return TweetCell.buildRootNode(self.tweets[indexPath.row], estimated: estimated)
144+
if indexPath.section == 0 {
145+
return TableRow<ClassicCell>(model: ClassicModel(title: "Hello Classic Cell") {
146+
print("action!")
147+
})
148+
} else {
149+
if let nodeModel = self.nodeModels?[indexPath.row] {
150+
return NodeTableRow(model: nodeModel.1)
151+
} else {
152+
return NodeTableRow(model: TweetCell.buildRootNode(self.tweets[indexPath.row], estimated: estimated))
153+
}
154+
}
202155
})
203156
}()
204157
}
205158

206159
extension TableViewController: UITableViewDelegate, UITableViewDataSource {
207160

161+
func numberOfSections(in tableView: UITableView) -> Int {
162+
return 2
163+
}
208164

209165
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
210-
return tweets.count
166+
if section == 0 {
167+
return 3
168+
} else {
169+
return tweets.count
170+
}
211171
}
212172

213173
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
214174
tableView.deselectRow(at: indexPath, animated: true)
215175
}
216176

217177
//adapted
218-
219178

220179
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
221-
let id = "cellId"
222-
return tableView.dequeueReusableCell(withIdentifier: id) ?? NodeTableViewCell(style: .default, reuseIdentifier: id)
180+
return adapter.tableView(tableView, cellForRowAt: indexPath)
223181
}
224182

225183
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
226-
let id = "headerId"
227-
return tableView.dequeueReusableHeaderFooterView(withIdentifier: id) ?? NodeTableHeaderFooterView(reuseIdentifier: id)
184+
return adapter.tableView(tableView, viewForHeaderInSection: section)
228185
}
229186

230187
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
231-
let id = "footerId"
232-
return tableView.dequeueReusableHeaderFooterView(withIdentifier: id) ?? NodeTableHeaderFooterView(reuseIdentifier: id)
188+
return adapter.tableView(tableView, viewForFooterInSection: section)
233189
}
234190

235191
//rows
192+
236193
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
237194
return adapter.tableView(tableView, estimatedHeightForRowAt: indexPath)
238195
}
@@ -241,11 +198,6 @@ extension TableViewController: UITableViewDelegate, UITableViewDataSource {
241198
return adapter.tableView(tableView, heightForRowAt: indexPath)
242199
}
243200

244-
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
245-
adapter.tableView(tableView, willDisplay: cell, forRowAt: indexPath)
246-
}
247-
248-
249201
//footers
250202
func tableView(_ tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat {
251203
return adapter.tableView(tableView, estimatedHeightForFooterInSection: section)
@@ -254,10 +206,6 @@ extension TableViewController: UITableViewDelegate, UITableViewDataSource {
254206
return adapter.tableView(tableView, heightForFooterInSection: section)
255207
}
256208

257-
func tableView(_ tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int) {
258-
adapter.tableView(tableView, willDisplayFooterView: view, forSection: section)
259-
}
260-
261209
//headers
262210

263211
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
@@ -268,9 +216,14 @@ extension TableViewController: UITableViewDelegate, UITableViewDataSource {
268216
return adapter.tableView(tableView, heightForHeaderInSection: section)
269217
}
270218

271-
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
272-
adapter.tableView(tableView, willDisplayHeaderView: view, forSection: section)
219+
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
220+
return true
221+
}
222+
223+
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
224+
273225
}
226+
274227
}
275228

276229
enum TweetCell {
@@ -398,10 +351,17 @@ enum TweetCell {
398351
Fix(tweetNode)
399352
)
400353

354+
if rootNode.frame.size.height > 10 { // in editing mode...
355+
let newTweetHeight = (rootNode.frame.height - pad - tweetNode.frame.minY)
356+
tweetNode.lx.set(height: newTweetHeight)
357+
}
358+
401359
timeStampNode.lx.firstBaselineAnchor.follow(userNode.lx.firstBaselineAnchor)
402360

403361
//calculate final cell height
404362
rootNode.frame.size.height = max(tweetNode.frame.maxY + pad, avatarNode.frame.maxY + pad)
363+
364+
405365
}
406366

407367
return rootNode
@@ -433,6 +393,7 @@ extension TweetCell {
433393
let paragraphStyle = NSMutableParagraphStyle()
434394
paragraphStyle.alignment = .left
435395
paragraphStyle.lineHeightMultiple = 1.2
396+
paragraphStyle.lineBreakMode = .byTruncatingTail
436397
let attributes = [
437398
NSParagraphStyleAttributeName: paragraphStyle,
438399
NSFontAttributeName: UIFont.systemFont(ofSize: 15.0),
@@ -516,3 +477,34 @@ extension RootNode {
516477
self.init(subnodes: [], layout: { _ in })
517478
}
518479
}
480+
481+
struct ClassicModel {
482+
let title: String
483+
let action: ()->Void
484+
init(title: String, action: @escaping ()->Void ) {
485+
self.title = title
486+
self.action = action
487+
}
488+
}
489+
490+
class ClassicCell: UITableViewCell, PresentationModelView {
491+
492+
var presentationModel: ClassicModel? {
493+
didSet {
494+
self.textLabel?.text = presentationModel?.title
495+
}
496+
}
497+
498+
override func setSelected(_ selected: Bool, animated: Bool) {
499+
super.setSelected(selected, animated: animated)
500+
if selected {
501+
presentationModel?.action()
502+
}
503+
}
504+
}
505+
506+
extension ClassicModel: PresentationModel {
507+
func calculate(for size: CGSize) -> CGSize {
508+
return CGSize(width: size.width, height: 50)
509+
}
510+
}

0 commit comments

Comments
 (0)