Digital PCT265 defect MVAPCT-272: Fix view async updates using Apple identity strategy.
This commit is contained in:
parent
fe0a83df0b
commit
190852601e
@ -11,51 +11,39 @@ import UIKit
|
||||
public struct MoleculeInfo: Hashable, CustomDebugStringConvertible {
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
hasher.combine(moleculeId)
|
||||
}
|
||||
|
||||
public static func == (lhs: MoleculeInfo, rhs: MoleculeInfo) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
lhs.moleculeId == rhs.moleculeId
|
||||
}
|
||||
|
||||
public let id: String
|
||||
public var moleculeId: String
|
||||
// TODO: This is an existing bug in the template handling. cellReuseId is a deep definition of the cell and its children. Structural changes to the cell should re-register.
|
||||
public let cellReuseId: String
|
||||
public let cellType: MoleculeViewProtocol.Type
|
||||
public var model: ListItemModelProtocol & MoleculeModelProtocol
|
||||
|
||||
init?(listItemModel: ListItemModelProtocol & MoleculeModelProtocol, delegateObject: MVMCoreUIDelegateObject? = nil) {
|
||||
guard let moleculeClass = ModelRegistry.getMoleculeClass(listItemModel)
|
||||
else { return nil }
|
||||
|
||||
self.model = listItemModel
|
||||
self.id = model.id
|
||||
self.moleculeId = listItemModel.id
|
||||
self.cellType = moleculeClass
|
||||
self.cellReuseId = moleculeClass.nameForReuse(with: listItemModel, delegateObject) ?? listItemModel.moleculeName
|
||||
}
|
||||
|
||||
func doesMatch(_ molecule: MoleculeModelProtocol) -> Bool {
|
||||
id == molecule.id
|
||||
}
|
||||
|
||||
func contains(oneOf moleculeModels: [MoleculeModelProtocol]) -> MoleculeModelProtocol? {
|
||||
model.findFirstMolecule(by: { existingMolecule in
|
||||
moleculeModels.contains { moleculeModel in
|
||||
existingMolecule.moleculeName == moleculeModel.moleculeName && existingMolecule.id == moleculeModel.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public var debugDescription: String {
|
||||
return "\(Self.self) \(id)"
|
||||
return "\(Self.self) \(moleculeId)"
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element == MoleculeInfo {
|
||||
|
||||
func filter(byMolecules molecules: [MoleculeModelProtocol]) -> [MoleculeInfo] {
|
||||
filter { listItemRef in molecules.contains { $0.id == listItemRef.model.id } }
|
||||
filter { listItemRef in molecules.contains { $0.id == listItemRef.moleculeId } }
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func registerTypes(with tableView: UITableView) -> [MoleculeInfo] {
|
||||
forEach { tableView.register($0.cellType, forCellReuseIdentifier: $0.cellReuseId) }
|
||||
return self
|
||||
@ -110,13 +98,14 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
|
||||
let tableView = super.createTableView()
|
||||
dataSource = UITableViewDiffableDataSource(tableView: tableView) { [weak self] tableView, indexPath, moleculeInfo in
|
||||
guard let self = self,
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.cellReuseId)
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.cellReuseId),
|
||||
let moleculeModel = molecule(for: moleculeInfo.moleculeId)
|
||||
else { return UITableViewCell() }
|
||||
cell.isHidden = moleculeInfo.model.gone
|
||||
cell.isHidden = (moleculeModel as? GoneableProtocol)?.gone == true
|
||||
(cell as? MoleculeViewProtocol)?.reset()
|
||||
(cell as? MoleculeListCellProtocol)?.setLines(with: templateModel?.line, delegateObject: delegateObjectIVar, additionalData: nil, indexPath: indexPath)
|
||||
if let moleculeView = cell as? MoleculeViewProtocol {
|
||||
updateMoleculeView(moleculeView, from: moleculeInfo.model)
|
||||
updateMoleculeView(moleculeView, from: moleculeModel)
|
||||
}
|
||||
(cell as? MVMCoreViewProtocol)?.updateView(tableView.bounds.width)
|
||||
// Neded to fix an apple defect where the cell is not the correct size on certain devices for certain cells
|
||||
@ -127,6 +116,13 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
|
||||
return tableView
|
||||
}
|
||||
|
||||
// TODO: This assumes a molecule is always present in the model tree. What if its in another data store? Also we should attempt O(1) for being in the render phase.
|
||||
func molecule(for id: String) -> MoleculeModelProtocol? {
|
||||
templateModel?.findFirstMolecule() { molecule in
|
||||
molecule.id == id
|
||||
}
|
||||
}
|
||||
|
||||
open override func parsePageJSON(loadObject: MVMCoreLoadObject) throws -> PageModelProtocol {
|
||||
return try parseTemplate(loadObject: loadObject)
|
||||
}
|
||||
@ -214,12 +210,15 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
|
||||
//--------------------------------------------------
|
||||
|
||||
open func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
return dataSource.itemIdentifier(for: indexPath)?.model.gone == true ? 0 : UITableView.automaticDimension
|
||||
guard let moleculeId = dataSource.itemIdentifier(for: indexPath)?.moleculeId,
|
||||
let moleculeModel = molecule(for: moleculeId) else { return 0 }
|
||||
return (moleculeModel as? GoneableProtocol)?.gone == true ? 0 : UITableView.automaticDimension
|
||||
}
|
||||
|
||||
open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
guard let moleculeInfo = dataSource.itemIdentifier(for: indexPath),
|
||||
let estimatedHeight = moleculeInfo.cellType.estimatedHeight(with: moleculeInfo.model, delegateObject() as? MVMCoreUIDelegateObject)
|
||||
let moleculeModel = molecule(for: moleculeInfo.moleculeId),
|
||||
let estimatedHeight = moleculeInfo.cellType.estimatedHeight(with: moleculeModel, delegateObject() as? MVMCoreUIDelegateObject)
|
||||
else { return 0 }
|
||||
|
||||
return estimatedHeight
|
||||
@ -300,14 +299,13 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
|
||||
}
|
||||
|
||||
var snapshot = dataSource.snapshot()
|
||||
let updatedListItems: [MoleculeInfo] = snapshot.itemIdentifiers.compactMap { listItemRef -> MoleculeInfo? in
|
||||
guard let matchedMolecule = listItemRef.contains(oneOf: molecules) else { return nil }
|
||||
if let matchedMolecule = matchedMolecule as? (ListItemModelProtocol & MoleculeModelProtocol), listItemRef.doesMatch(matchedMolecule) {
|
||||
var listItemRef = listItemRef
|
||||
listItemRef.model = matchedMolecule // Replace the top level molecule if it changed.
|
||||
return listItemRef
|
||||
}
|
||||
return listItemRef
|
||||
let updatedListItems: [MoleculeInfo] = snapshot.itemIdentifiers.filter { listItemRef -> Bool in
|
||||
guard let model = molecule(for: listItemRef.moleculeId) else { return false }
|
||||
return model.findFirstMolecule(by: { existingMolecule in
|
||||
molecules.contains { moleculeModel in
|
||||
existingMolecule.moleculeName == moleculeModel.moleculeName && existingMolecule.id == moleculeModel.id
|
||||
}
|
||||
}) != nil
|
||||
}
|
||||
if #available(iOS 15.0, *) {
|
||||
snapshot.reconfigureItems(updatedListItems)
|
||||
@ -315,6 +313,8 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
|
||||
// A full reload can cause a flicker / animation. Better to avoid with above reconfigure method.
|
||||
snapshot.reloadItems(updatedListItems)
|
||||
}
|
||||
// TODO: Thoughts on fading on cell structural replacements?
|
||||
// dataSource.defaultRowAnimation = .fade
|
||||
dataSource.apply(snapshot)
|
||||
|
||||
// Refresh the cell. (reload loses cell selection). TODO: Check if necessary and timing.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user