
рд╕рд╛рде рдХрд╛рдо рдХрд░рддреЗ рд╕рдордп, UITableView
рдореИрдВ рдЯреЗрдореНрдкрд▓реЗрдЯ рдХреЛрдб рд▓рд┐рдЦрдиреЗ рд╕реЗ рдмрдЪрдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛, рдЬреЛ рдХрд┐ рдФрд░ рднреА рдЬрдЯрд┐рд▓ рд╣реИ рдпрджрд┐ рдЖрдкрдХреЛ рддрд╛рд▓рд┐рдХрд╛ рдХреА рд╕реНрдерд┐рддрд┐ рдХреЛ рдПрдирд┐рдореЗрдЯреЗрдб рд░реВрдк рд╕реЗ рдЕрдкрдбреЗрдЯ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред Apple рдиреЗ WWDC 2019 рдореЗрдВ рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХрд╛ рд╕рдорд╛рдзрд╛рди рдкреНрд░рд╕реНрддреБрдд рдХрд┐рдпрд╛, рд▓реЗрдХрд┐рди рдпрд╣ рдХреЗрд╡рд▓ iOS 13. рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ рдФрд░ рд╣рдо, рдПрдХ рдореЛрдмрд╛рдЗрд▓ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдбреЗрд╡рд▓рдкрдореЗрдВрдЯ рд╕реНрдЯреВрдбрд┐рдпреЛ рдХреЗ рд░реВрдк рдореЗрдВ, iOS рдХреЗ рдиреНрдпреВрдирддрдо рд╕рдВрд╕реНрдХрд░рдг рдХреЛ рдЪреБрдирдиреЗ рдХреА рд▓рдХреНрдЬрд░реА рдирд╣реАрдВ рд░рдЦрддреЗ рд╣реИрдВред
рдЗрд╕рд▓рд┐рдП, рд╣рдордиреЗ рд░рд╛рд╕реНрддреЗ рдореЗрдВ рдХреЛрд╢рд┐рдХрд╛рдУрдВ рдХреЗ рд╡рд┐рдиреНрдпрд╛рд╕ рдХреЛ рд╕рд░рд▓ рдХрд░рддреЗ рд╣реБрдП, рддрд╛рд▓рд┐рдХрд╛рдУрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдбреЗрдЯрд╛-рд╕рдВрдЪрд╛рд▓рд┐рдд рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХреА рд╣рдорд╛рд░реА рджреГрд╖реНрдЯрд┐ рдХрд╛ рдПрд╣рд╕рд╛рд╕ рдХрд┐рдпрд╛ред рдФрд░ рдЙрдиреНрд╣реЛрдВрдиреЗ рдПрдХ рдПрдирд┐рдореЗрдЯреЗрдб рдЯреЗрдмрд▓ рдЕрдкрдбреЗрдЯ рдЬреЛрдбрд╝рд╛, рдЬреЛ рдХрд┐ рд░реИрдЦрд┐рдХ рд╕рдордп рдУ (рдПрди) рдХреЗ рд▓рд┐рдП рдкреБрд░рд╛рдиреЗ рдФрд░ рдирдП рдбреЗрдЯрд╛ рдХреЗ рдмреАрдЪ рдЕрдВрддрд░ рдХреА рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдЧрдгрдирд╛ рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИ ред рдпрд╣ рд╕рдм рд╣рдордиреЗ рдПрдХ рдЫреЛрдЯреЗ рд╕реЗ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдореЗрдВ рдбрд┐рдЬрд╝рд╛рдЗрди рдХрд┐рдпрд╛ рд╣реИ рдЬрд┐рд╕реЗ рдЯреЗрдмрд▓ рдПрдбреЗрдкреНрдЯрд░ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИред
рд╣рдордиреЗ рдХреНрдпрд╛ рдХрд┐рдпрд╛ рдФрд░ рд╣рдо рдХреИрд╕реЗ рдЖрдП, рдЗрд╕ рдкрд░ рд▓реЗрдЦ рдореЗрдВ рдЪрд░реНрдЪрд╛ рдХреА рдЬрд╛рдПрдЧреАред
TableAdapter рдХреЗ рдкреЗрд╢реЗрд╡рд░реЛрдВ рдФрд░ рд╡рд┐рдкрдХреНрд╖
рдкреЗрд╢реЗрд╡рд░реЛрдВ
- рдПрдиреАрдореЗрд╢рди рддрд╛рд▓рд┐рдХрд╛ рдЕрджреНрдпрддрди
- рд░реИрдЦрд┐рдХ рд╕рдордп рдореЗрдВ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рдЕрдВрддрд░ рдЧрдгрдирд╛
- рдХреЛрдИ рддрд╛рд▓рд┐рдХрд╛, рд╕реЗрд▓ рдпрд╛ рдореЙрдбрд▓ рдЗрдирд╣реЗрд░рд┐рдЯ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ
- рдЕрдм рдФрд░ рдирд╣реАрдВ
dequeReusable...
- рдкреНрд░рдХрд╛рд░-рд╕реБрд░рдХреНрд╖рд┐рдд рд╕реЗрд▓ рд╕реЗрдЯрдЕрдк, рд╣реЗрдбрд░ / рдкрд╛рдж
- рд╕реЗрд▓ рдЖрд░рдВрднреАрдХрд░рдг рдХрд┐рд╕реА рднреА рддрд░рд╣ рд╕реЗ
- рдЖрд╕рд╛рди рдЕрдиреБрднрд╛рдЧ рд╕реЗрдЯрдЕрдк
- рд╡рд┐рд╕реНрддрд╛рд░ рдХрд░рдиреЗ рдореЗрдВ рдЖрд╕рд╛рди
minuses
, Hashable
. , - Swift, .. . - .
тАУ :
1. , /
1.1
, , Hashable
.
extension User: Hashable { ... }
1.2
Configurable
, . , .
extension Cell: Configurable {
public func setup(with item: User) {
textLabel?.text = item.name
}
}
, , , . , setup(with:)
.
1.3 /
/ Configurable
, . / 1.3 .
2.
() , /. Section<Item, SectionId>
, (Item
) (SectionId
). (id) Hashable
, .
/
/ header
/footer
.
/
/ headerIdentifier
/footerIdentifier
, . , Configurable
( 1.3).
let section = Section<User, Int>(
id: 0,
items: users,
header: "Users Section",
footer: "Users Section",
headerIdentifier: "HeaderReuseIdentifier",
footerIdentifier: "FooterReuseIdentifier"
)
id
.
3.
TableAdapter<Item, SectionId>
, (Item
) (SectionId
). , .
CellReuseIdentifierProvider
, . , IndexPath , .
ellDidSelectHandler
, , IndexPath .
lazy var adapter = TableAdapter<User, Int>(
tableView: tableView,
cellIdentifierProvider: { (indexPath, item) -> String? in
},
cellDidSelectHandler: { [weak self] (table, indexPath, item) in
}
)
:
adapter.update(with: [section], animated: true)
class ViewController: UIViewController {
let tableView = ...
lazy var adapter = TableAdapter<User, Int>(
tableView: tableView,
cellIdentifierProvider: { (indexPath, item) -> String? in
return "CellReuseIdentifier"
},
cellDidSelectHandler: { [weak self] (table, indexPath, item) in
}
)
let users: [User] = [...]
override func viewDidLoad() {
super.viewDidLoad()
setupTable()
let section = Section<User, Int>(
id: 0,
items: users,
header: "Users Section",
footer: "Users Section",
headerIdentifier: "HeaderReuseIdentifier",
footerIdentifier: "FooterReuseIdentifier"
)
adapter.update(with: [section]], animated: true)
}
func setupTable() {
tableView.register(
Cell.self,
forCellReuseIdentifier: "CellReuseIdentifier"
)
tableView.register(
Header.self,
forHeaderFooterViewReuseIdentifier identifier: "HeaderReuseIdentifier"
)
tableView.register(
Footer.self,
forHeaderFooterViewReuseIdentifier identifier: "FooterReuseIdentifier"
)
}
}
Hashable
, (associated value).
, CellReuserIdentifierProvider
. A , "Cell" :
tableView.register(
Cell.self,
forCellReuseIdentifier: adapter.defaultCellIdentifier
)
/
headerIdentifier
/footerIdentifier
. / , "Header"
/"Footer"
.
tableView.register(
HeaderView.self,
forHeaderFooterViewReuseIdentifier identifier: adapter.defaultHeaderIdentifier
)
tableView.register(
FooterView.self,
forHeaderFooterViewReuseIdentifier identifier: adapter.defaultFooterIdentifier
)
Sender
, , , , TableAdapter'a sender
. , / SenderConfigurable
.
class ViewController: UIViewController {
lazy var adapter = TableAdapter<User, Int>(
tableView: tableView,
sender: self
)
}
extension Cell: SenderConfigurable {
func setup(with item: User, sender: ViewController) {
textLabel?.text = item.name
delegate = sender
}
}
: , . , setup(with:sender:)
.
, CellProvider
, :
lazy var adapter = TableAdapter<User, Int>(
tableView: tableView,
cellProvider: { (table, indexPath, user) in
let cell = table.dequeueReusableCell(
withIdentifier: "CellReuseIdentifier",
for: indexPath
) as! Cell
cell.setup(with: user)
return cell
}
)
/ TableAdapter
.
(diff) , . , A technique for isolating differences between files. O(n) . Hashable
.
, , performBatchUpdates(_:completion:)
. . .
- () (id). .
let sectionsDiff = try calculateDiff(form: oldSections, to: newSections)
- , , . , , id. , , , . , , .
let intermediateSections = applyDiff(sectionsDiff, to: oldSections)
- ( id) . . , , .
let rowsDiff = try calculateRowsDiff(from: intermediateSections, to: newSections)
:
let diff = Diff(
sections: sectionsDiff,
rows: rowsDiff,
intermediateData: intermediateSections,
resultData: newSections
)
- : ( ) , .
data = diff.intermediateData
tableView.insertSections(diff.sections.inserts, with: animationType)
tableView.deleteSections(diff.sections.deletes, with: animationType)
diff.sections.moves.forEach { tableView.moveSection($0.from, toSection: $0.to) }
- : () , .
data = diff.resultData
tableView.deleteRows(at: diff.rows.deletes, with: animationType)
tableView.insertRows(at: diff.rows.inserts, with: animationType)
diff.rows.moves.forEach { tableView.moveRow(at: $0.from, to: $0.to) }
1: . ┬л┬╗ ┬л┬╗ .

2: , .. . , , , , . .. , . adapter.update(with: sections, animated: false)
.
Configurable
, :
let user = users[indexPath.row]
let cell = tableView.dequeueReusableCell(
withIdentifier: cellId,
for: indexPath
) as! Cell
cell.setup(with: user)
. (associatedtype).
Configurable
, :
protocol Configurable {
associatedtype ItemType: Any
func setup(with item: ItemType)
}
, dequeueReusableCell(withIdentifier:for:)
Configurable
- ItemType
. AnyConfigurable
:
protocol AnyConfigurable {
func anySetup(with item: Any)
}
, :
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
if let cell = cell as? AnyConfigurable {
cell.anySetup(with: item)
}
Configurable
AnyConfigurable
:
extension Configurable {
func anySetup(with item: Any) {
if let item = item as? ItemType {
setup(with: item)
}
}
}
, - ┬л┬╗ , . , .
, Swift type-erased wrappers. AnyHashable, AnyIndex.
: . : .
GitHub
рдирддреАрдЬрддрди, рд╣рдореЗрдВ рдПрдХ рд╕рдорд╛рдзрд╛рди рдорд┐рд▓рд╛ рдЬреЛ рдЖрдкрдХреЛ рдЖрд╕рд╛рдиреА рд╕реЗ рдХреЛрд╢рд┐рдХрд╛рдУрдВ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ, рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛ рдХрдо рдЯреЗрдореНрдкрд▓реЗрдЯ рдХреЛрдб рдФрд░ рдПрдиреАрдореЗрд╢рди рд▓рд┐рдЦрддрд╛ рд╣реИред рдЙрд╕реА рд╕рдордп, рдирд┐рдореНрди-рд╕реНрддрд░реАрдп рддрд╛рд▓рд┐рдХрд╛ рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдФрд░ рдпрджрд┐ рдЖрд╡рд╢реНрдпрдХ рд╣реЛ рддреЛ рдХрд╛рд░реНрдпрд╛рддреНрдордХ рд╡рд┐рд╕реНрддрд╛рд░ рдХреА рд╕рдВрднрд╛рд╡рдирд╛ рдмрдиреА рд░рд╣реАред
GitHub рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди