add and remove clean up, tabs addition

This commit is contained in:
Pfeil, Scott Robert 2021-07-26 18:51:22 -04:00
parent e853647367
commit dc12612c6f
11 changed files with 163 additions and 186 deletions

View File

@ -513,6 +513,7 @@
D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */; }; D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */; };
D2D90B42240463E100DD6EC9 /* MoleculeHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */; }; D2D90B42240463E100DD6EC9 /* MoleculeHeaderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */; };
D2D90B442404789000DD6EC9 /* MoleculeContainerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D90B432404789000DD6EC9 /* MoleculeContainerProtocol.swift */; }; D2D90B442404789000DD6EC9 /* MoleculeContainerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D90B432404789000DD6EC9 /* MoleculeContainerProtocol.swift */; };
D2E0FFF826AF68530085D696 /* UITableViewRowAnimation+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E0FFF726AF68530085D696 /* UITableViewRowAnimation+Extension.swift */; };
D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADA2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift */; }; D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADA2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift */; };
D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */; }; D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */; };
D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */; }; D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */; };
@ -1082,6 +1083,7 @@
D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTemplate.swift; sourceTree = "<group>"; }; D2D6CD4122E78FAB00D701B8 /* ThreeLayerTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTemplate.swift; sourceTree = "<group>"; };
D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeHeaderModel.swift; sourceTree = "<group>"; }; D2D90B41240463E100DD6EC9 /* MoleculeHeaderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeHeaderModel.swift; sourceTree = "<group>"; };
D2D90B432404789000DD6EC9 /* MoleculeContainerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeContainerProtocol.swift; sourceTree = "<group>"; }; D2D90B432404789000DD6EC9 /* MoleculeContainerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeContainerProtocol.swift; sourceTree = "<group>"; };
D2E0FFF726AF68530085D696 /* UITableViewRowAnimation+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewRowAnimation+Extension.swift"; sourceTree = "<group>"; };
D2E1FADA2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIDelegateObject.swift; sourceTree = "<group>"; }; D2E1FADA2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MVMCoreUIDelegateObject.swift; sourceTree = "<group>"; };
D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTableViewController.swift; sourceTree = "<group>"; }; D2E1FADE2268B8E700AEFD8C /* ThreeLayerTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeLayerTableViewController.swift; sourceTree = "<group>"; };
D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListTemplate.swift; sourceTree = "<group>"; }; D2E1FAE02268E81D00AEFD8C /* MoleculeListTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeListTemplate.swift; sourceTree = "<group>"; };
@ -1486,6 +1488,7 @@
0AB764D224460FA400E7FE72 /* UIPickerView+Extension.swift */, 0AB764D224460FA400E7FE72 /* UIPickerView+Extension.swift */,
D2ED27E6254B0CE600A1C293 /* UIAlertActionStyle+Codable.swift */, D2ED27E6254B0CE600A1C293 /* UIAlertActionStyle+Codable.swift */,
D2ED27E7254B0CE600A1C293 /* UIAlertControllerStyle+Extension.swift */, D2ED27E7254B0CE600A1C293 /* UIAlertControllerStyle+Extension.swift */,
D2E0FFF726AF68530085D696 /* UITableViewRowAnimation+Extension.swift */,
); );
path = Extensions; path = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
@ -2942,6 +2945,7 @@
BB2BF0EC2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift in Sources */, BB2BF0EC2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift in Sources */,
943784F6236B77BB006A1E82 /* WheelAnimationHandler.swift in Sources */, 943784F6236B77BB006A1E82 /* WheelAnimationHandler.swift in Sources */,
011D95A1240453D0000E3791 /* RuleEqualsModel.swift in Sources */, 011D95A1240453D0000E3791 /* RuleEqualsModel.swift in Sources */,
D2E0FFF826AF68530085D696 /* UITableViewRowAnimation+Extension.swift in Sources */,
1D6D258826899B0C00DEBB08 /* ImageButtonModel.swift in Sources */, 1D6D258826899B0C00DEBB08 /* ImageButtonModel.swift in Sources */,
AA07EA912510A442009A2AE3 /* StarModel.swift in Sources */, AA07EA912510A442009A2AE3 /* StarModel.swift in Sources */,
D29DF2AA21E7B2F9003B2FB9 /* MVMCoreUIConstants.m in Sources */, D29DF2AA21E7B2F9003B2FB9 /* MVMCoreUIConstants.m in Sources */,

View File

@ -1,9 +1,9 @@
// //
// UITableViewRowAnimation.swift // UITableViewRowAnimation+Extension.swift
// MVMCoreUI // MVMCoreUI
// //
// Created by Scott Pfeil on 7/26/21. // Created by Scott Pfeil on 7/26/21.
// Copyright © 2021 Verizon Wireless. All rights reserved. // Copyright © 2021 Verizon Wireless. All rights reserved.
// //
import Foundation extension UITableView.RowAnimation: Codable {}

View File

@ -68,22 +68,22 @@ class AccordionListItemModel: MoleculeListItemModel {
extension AccordionListItemModel: PageBehaviorProtocolRequirer { extension AccordionListItemModel: PageBehaviorProtocolRequirer {
public func getRequiredBehaviors() -> [PageBehaviorModelProtocol] { public func getRequiredBehaviors() -> [PageBehaviorModelProtocol] {
return [AddRemoveMoleculeBehaviorModel()] return [AddRemoveMoleculesBehaviorModel()]
} }
} }
extension AccordionListItemModel: AddMolecules { extension AccordionListItemModel: AddMolecules {
public func moleculesToAdd() -> ([ListItemModelProtocol & MoleculeModelProtocol], AddMoleculePosition, UITableView.RowAnimation?)? { public func moleculesToAdd() -> AddMolecules.AddParameters? {
guard !added, selected else { return nil } guard !added, selected else { return nil }
added = true added = true
return (molecules, .below, .automatic) return (molecules, .below)
} }
} }
extension AccordionListItemModel: RemoveMolecules { extension AccordionListItemModel: RemoveMolecules {
public func moleculesToRemove() -> ([ListItemModelProtocol & MoleculeModelProtocol], UITableView.RowAnimation?)? { public func moleculesToRemove() -> [ListItemModelProtocol & MoleculeModelProtocol]? {
guard added else { return nil } guard added else { return nil }
added = false added = false
return (molecules, .automatic) return molecules
} }
} }

View File

@ -46,9 +46,9 @@
model.selected = accordionButton.isSelected model.selected = accordionButton.isSelected
if accordionButton.isSelected { if accordionButton.isSelected {
MVMCoreActionHandler.shared()?.handleAction(AddMoleculesActionModel.identifier, actionInformation: nil, additionalData: [KeySourceModel: model], delegateObject: delegateObject) MVMCoreActionHandler.shared()?.asyncHandleAction(with: AddMoleculesActionModel(.automatic), additionalData: [KeySourceModel: model], delegateObject: delegateObject)
} else { } else {
MVMCoreActionHandler.shared()?.handleAction(RemoveMoleculesActionModel.identifier, actionInformation: nil, additionalData: [KeySourceModel: model], delegateObject: delegateObject) MVMCoreActionHandler.shared()?.asyncHandleAction(with: RemoveMoleculesActionModel(.automatic), additionalData: [KeySourceModel: model], delegateObject: delegateObject)
} }
if (accordionListItemModel?.hideLineWhenExpanded ?? false) && (self.bottomSeparatorView?.shouldBeVisible() ?? false) { if (accordionListItemModel?.hideLineWhenExpanded ?? false) && (self.bottomSeparatorView?.shouldBeVisible() ?? false) {
@ -60,5 +60,10 @@
super.set(with: model, delegateObject, additionalData) super.set(with: model, delegateObject, additionalData)
guard let model = model as? AccordionListItemModel else { return } guard let model = model as? AccordionListItemModel else { return }
accordionButton.isSelected = model.selected accordionButton.isSelected = model.selected
accordionButton.setTitle(accordionButton.isSelected ? "-" : "+", for: .normal)
if (accordionListItemModel?.hideLineWhenExpanded ?? false) && (self.bottomSeparatorView?.shouldBeVisible() ?? false) {
bottomSeparatorView?.isHidden = accordionButton.isSelected
}
} }
} }

View File

@ -32,10 +32,9 @@ import UIKit
MVMCoreDispatchUtility.performBlock(inBackground: { MVMCoreDispatchUtility.performBlock(inBackground: {
if let oldValue = oldValue, if let oldValue = oldValue,
oldValue.count > 0 { oldValue.count > 0 {
MVMCoreUIActionHandler.shared()?.synchronouslyHandleAction(RemoveMoleculesActionModel.identifier, actionInformation: nil, additionalData: [KeySourceModel: model], delegateObject: self.delegateObject) MVMCoreActionHandler.shared()?.syncHandleAction(with: RemoveMoleculesActionModel(.fade), additionalData: [KeySourceModel: model], delegateObject: self.delegateObject)
} }
MVMCoreActionHandler.shared()?.syncHandleAction(with: AddMoleculesActionModel(.fade), additionalData: [KeySourceModel: model], delegateObject: self.delegateObject)
MVMCoreActionHandler.shared()?.handleAction(AddMoleculesActionModel.identifier, actionInformation: nil, additionalData: [KeySourceModel: model], delegateObject: self.delegateObject)
}) })
} }
} }

View File

@ -86,24 +86,24 @@ import Foundation
extension DropDownListItemModel: PageBehaviorProtocolRequirer { extension DropDownListItemModel: PageBehaviorProtocolRequirer {
public func getRequiredBehaviors() -> [PageBehaviorModelProtocol] { public func getRequiredBehaviors() -> [PageBehaviorModelProtocol] {
return [AddRemoveMoleculeBehaviorModel()] return [AddRemoveMoleculesBehaviorModel()]
} }
} }
extension DropDownListItemModel: AddMolecules { extension DropDownListItemModel: AddMolecules {
public func moleculesToAdd() -> ([ListItemModelProtocol & MoleculeModelProtocol], AddMoleculePosition, UITableView.RowAnimation?)? { public func moleculesToAdd() -> AddMolecules.AddParameters? {
guard addedMolecules == nil, guard addedMolecules == nil,
let index = dropDown.selectedIndex else { return nil } let index = dropDown.selectedIndex else { return nil }
let addedMolecules = molecules[index] let addedMolecules = molecules[index]
self.addedMolecules = addedMolecules self.addedMolecules = addedMolecules
return (addedMolecules, .below, .fade) return (addedMolecules, .below)
} }
} }
extension DropDownListItemModel: RemoveMolecules { extension DropDownListItemModel: RemoveMolecules {
public func moleculesToRemove() -> ([ListItemModelProtocol & MoleculeModelProtocol], UITableView.RowAnimation?)? { public func moleculesToRemove() -> [ListItemModelProtocol & MoleculeModelProtocol]? {
guard let addedMolecules = addedMolecules else { return nil } guard let addedMolecules = addedMolecules else { return nil }
self.addedMolecules = nil self.addedMolecules = nil
return (addedMolecules, .fade) return addedMolecules
} }
} }

View File

@ -17,7 +17,8 @@ public class TabsListItemModel: ListItemModel, MoleculeModelProtocol {
public static var identifier: String = "tabsListItem" public static var identifier: String = "tabsListItem"
var tabs: TabsModel var tabs: TabsModel
var molecules: [[ListItemModelProtocol & MoleculeModelProtocol]] var molecules: [[ListItemModelProtocol & MoleculeModelProtocol]]
private var addedMolecules: [ListItemModelProtocol & MoleculeModelProtocol]?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
//-------------------------------------------------- //--------------------------------------------------
@ -70,3 +71,26 @@ public class TabsListItemModel: ListItemModel, MoleculeModelProtocol {
try container.encodeModels2D(molecules, forKey: .molecules) try container.encodeModels2D(molecules, forKey: .molecules)
} }
} }
extension TabsListItemModel: PageBehaviorProtocolRequirer {
public func getRequiredBehaviors() -> [PageBehaviorModelProtocol] {
return [AddRemoveMoleculesBehaviorModel()]
}
}
extension TabsListItemModel: AddMolecules {
public func moleculesToAdd() -> AddMolecules.AddParameters? {
guard addedMolecules == nil else { return nil }
let addedMolecules = molecules[tabs.selectedIndex]
self.addedMolecules = addedMolecules
return (addedMolecules, .below)
}
}
extension TabsListItemModel: RemoveMolecules {
public func moleculesToRemove() -> [ListItemModelProtocol & MoleculeModelProtocol]? {
guard let addedMolecules = addedMolecules else { return nil }
self.addedMolecules = nil
return addedMolecules
}
}

View File

@ -55,9 +55,9 @@ import UIKit
// TODO: Move to AddRemoveMoleculeBehaviorModel // TODO: Move to AddRemoveMoleculeBehaviorModel
extension TabsTableViewCell: TabsDelegate { extension TabsTableViewCell: TabsDelegate {
public func shouldSelectItem(_ indexPath: IndexPath, tabs: Tabs) -> Bool { public func shouldSelectItem(_ indexPath: IndexPath, tabs: Tabs) -> Bool {
guard indexPath.row != tabs.selectedIndex else { return false }
if let model = tabsListItemModel { if let model = tabsListItemModel {
let molecules = model.molecules[tabs.selectedIndex] MVMCoreActionHandler.shared()?.asyncHandleAction(with: RemoveMoleculesActionModel(indexPath.row < tabs.selectedIndex ? .right : .left), additionalData: [KeySourceModel: model], delegateObject: delegateObject)
delegateObject?.moleculeListDelegate?.removeMolecules(molecules, animation: indexPath.row < tabs.selectedIndex ? .right : .left)
} }
previousTabIndex = tabs.selectedIndex previousTabIndex = tabs.selectedIndex
return true return true
@ -65,12 +65,9 @@ extension TabsTableViewCell: TabsDelegate {
public func didSelectItem(_ indexPath: IndexPath, tabs: Tabs) { public func didSelectItem(_ indexPath: IndexPath, tabs: Tabs) {
let index = indexPath.row let index = indexPath.row
if let model = tabsListItemModel, guard let model = tabsListItemModel,
index < model.molecules.count, index < model.molecules.count else { return }
let cellIndexPath = delegateObject?.moleculeListDelegate?.getIndexPath(for: model) { MVMCoreActionHandler.shared()?.asyncHandleAction(with: AddMoleculesActionModel(index < previousTabIndex ? .left : .right), additionalData: [KeySourceModel: model], delegateObject: delegateObject)
let molecules = model.molecules[index]
delegateObject?.moleculeListDelegate?.addMolecules(molecules, indexPath: cellIndexPath, animation: index < previousTabIndex ? .left : .right)
}
} }
} }

View File

@ -182,44 +182,6 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
// MARK: - MoleculeDelegateProtocol // MARK: - MoleculeDelegateProtocol
//-------------------------------------------------- //--------------------------------------------------
open override func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) {
guard let tableView = tableView else { return }
if let indexPath = tableView.indexPathForRow(at: molecule.center), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false {
performTableViewUpdates()
}
}
open override func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? {
guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in
//TODO: check for molecule protocol eqaulity
let json = moleculeInfo.molecule.toJSON()
return json == molecule.toJSON()
}) else { return nil }
return IndexPath(row: index, section: 0)
}
open override func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) {
// This dispatch is needed to fix a race condition that can occur if this function is called during the table setup.
DispatchQueue.main.async {
var indexPaths: [IndexPath] = []
for molecule in molecules {
if let info = self.createMoleculeInfo(with: molecule) {
self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier)
let index = indexPath.row + 1 + indexPaths.count
self.moleculesInfo?.insert(info, at: index)
indexPaths.append(IndexPath(row: index, section: 0))
}
}
guard indexPaths.count > 0 else { return }
self.tableView?.insertRows(at: indexPaths, with: animation)
self.updateViewConstraints()
self.view.layoutIfNeeded()
}
}
open func newData(for molecule: MoleculeModelProtocol) { open func newData(for molecule: MoleculeModelProtocol) {
//TODO: expand for header, navigation, etc //TODO: expand for header, navigation, etc
let json = molecule.toJSON() let json = molecule.toJSON()
@ -252,23 +214,6 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
tableView.selectRow(at: selectedIndex, animated: false, scrollPosition: .none) tableView.selectRow(at: selectedIndex, animated: false, scrollPosition: .none)
} }
} }
open override func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) {
var indexPaths: [IndexPath] = []
//TODO: check for molecule protocol equality
for molecule in molecules {
if let removeIndex = moleculesInfo?.firstIndex(where: { molecule.toJSON() == $0.molecule.toJSON() }) {
moleculesInfo?.remove(at: removeIndex)
indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0))
}
}
guard indexPaths.count > 0 else { return }
tableView?.deleteRows(at: indexPaths, with: animation)
updateViewConstraints()
view.layoutIfNeeded()
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Convenience // MARK: - Convenience
@ -337,91 +282,57 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
} }
extension MoleculeListTemplate: MoleculeListProtocol { extension MoleculeListTemplate: MoleculeListProtocol {
open override func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) { open func removeMolecules(at indexPaths: [IndexPath], animation: UITableView.RowAnimation?) {
guard let tableView = tableView else { return } for (index, indexPath) in indexPaths.sorted().enumerated() {
if let indexPath = tableView.indexPathForRow(at: molecule.center), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false { let removeIndex = indexPath.row - index
performTableViewUpdates() moleculesInfo?.remove(at: removeIndex)
} }
}
open override func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? { guard let animation = animation,
guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in indexPaths.count > 0 else { return }
//TODO: check for molecule protocol eqaulity tableView?.deleteRows(at: indexPaths, with: animation)
let json = moleculeInfo.molecule.toJSON() updateViewConstraints()
return json == molecule.toJSON() view.layoutIfNeeded()
}) else { return nil } }
open func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation?) {
// This dispatch is needed to fix a race condition that can occur if this function is called during the table setup.
DispatchQueue.main.async {
var indexPaths: [IndexPath] = []
return IndexPath(row: index, section: 0) for molecule in molecules {
} if let info = self.createMoleculeInfo(with: molecule) {
self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier)
let index = indexPath.row + indexPaths.count
self.moleculesInfo?.insert(info, at: index)
indexPaths.append(IndexPath(row: index, section: 0))
}
}
open override func addMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], indexPath: IndexPath, animation: UITableView.RowAnimation) { guard let animation = animation,
// This dispatch is needed to fix a race condition that can occur if this function is called during the table setup. indexPaths.count > 0 else { return }
DispatchQueue.main.async { self.tableView?.insertRows(at: indexPaths, with: animation)
var indexPaths: [IndexPath] = [] self.updateViewConstraints()
self.view.layoutIfNeeded()
}
}
open func getIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol) -> IndexPath? {
guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in
//TODO: check for molecule protocol eqaulity
let json = moleculeInfo.molecule.toJSON()
return json == molecule.toJSON()
}) else { return nil }
return IndexPath(row: index, section: 0)
}
for molecule in molecules { open func moleculeLayoutUpdated(_ molecule: MoleculeViewProtocol) {
if let info = self.createMoleculeInfo(with: molecule) { guard let tableView = tableView else { return }
self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier)
let index = indexPath.row + 1 + indexPaths.count
self.moleculesInfo?.insert(info, at: index)
indexPaths.append(IndexPath(row: index, section: 0))
}
}
guard indexPaths.count > 0 else { return } let point = molecule.convert(molecule.bounds.origin, to: tableView)
self.tableView?.insertRows(at: indexPaths, with: animation) if let indexPath = tableView.indexPathForRow(at: point), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false {
self.updateViewConstraints() performTableViewUpdates()
self.view.layoutIfNeeded() }
} }
}
open func newData(for molecule: MoleculeModelProtocol) {
//TODO: expand for header, navigation, etc
let json = molecule.toJSON()
guard let index = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in
//TODO: check for molecule protocol eqaulity
if json == moleculeInfo.molecule.toJSON() {
return true
} else if let parent = moleculeInfo.molecule as? ParentMoleculeModelProtocol {
// Get all molecules of the same type for faster check.
let molecules: [MoleculeModelProtocol] = parent.reduceDepthFirstTraverse(options: .childFirst, depth: 0, initialResult: []) { (accumulator, currentMolecule, depth) in
if currentMolecule.moleculeName == molecule.moleculeName {
return accumulator + [currentMolecule]
}
return accumulator
}
for molecule in molecules {
if json == molecule.toJSON() {
return true
}
}
}
return false
}) else { return }
// Refresh the cell. (reload loses cell selection)
let selectedIndex = tableView.indexPathForSelectedRow
let indexPath = IndexPath(row: index, section: 0)
tableView.reloadRows(at: [indexPath], with: .automatic)
if let selectedIndex = selectedIndex {
tableView.selectRow(at: selectedIndex, animated: false, scrollPosition: .none)
}
}
open override func removeMolecules(_ molecules: [ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation) {
var indexPaths: [IndexPath] = []
//TODO: check for molecule protocol equality
for molecule in molecules {
if let removeIndex = moleculesInfo?.firstIndex(where: { molecule.toJSON() == $0.molecule.toJSON() }) {
moleculesInfo?.remove(at: removeIndex)
indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0))
}
}
guard indexPaths.count > 0 else { return }
tableView?.deleteRows(at: indexPaths, with: animation)
updateViewConstraints()
view.layoutIfNeeded()
}
} }

View File

@ -8,25 +8,28 @@
import Foundation import Foundation
public enum AddMoleculePosition { public enum AddMoleculesPosition {
case above case above
case below case below
} }
/// Protocol for adding items to a list
public protocol AddMolecules { public protocol AddMolecules {
typealias AddParameters = ([ListItemModelProtocol & MoleculeModelProtocol], AddMoleculesPosition)
/// Get the molecules that need to be added, the position to add them to, and the animation to use /// Get the molecules that need to be added, the position to add them to, and the animation to use
func moleculesToAdd() -> ([ListItemModelProtocol & MoleculeModelProtocol], AddMoleculePosition, UITableView.RowAnimation?)? func moleculesToAdd() -> AddParameters?
} }
/// Protocol for removing items to a list
public protocol RemoveMolecules { public protocol RemoveMolecules {
/// Get the molecules that need to be removed, and the animation to use /// Get the molecules that need to be removed, and the animation to use
func moleculesToRemove() -> ([ListItemModelProtocol & MoleculeModelProtocol], UITableView.RowAnimation?)? func moleculesToRemove() -> [ListItemModelProtocol & MoleculeModelProtocol]?
} }
public extension AddMolecules { public extension AddMolecules {
/// Convenience function that gets the molecules that need to be added, recursively checking for additional molecules to add.
/// Gets the molecules that need to be added, recursively checking for additional molecules to add. func getRecursiveMoleculesToAdd() -> AddMolecules.AddParameters? {
func getRecursiveMoleculesToAdd() -> ([ListItemModelProtocol & MoleculeModelProtocol], AddMoleculePosition, UITableView.RowAnimation?)? {
guard let moleculesToAdd = moleculesToAdd() else { return nil } guard let moleculesToAdd = moleculesToAdd() else { return nil }
var newMolecules: [ListItemModelProtocol & MoleculeModelProtocol] = [] var newMolecules: [ListItemModelProtocol & MoleculeModelProtocol] = []
for molecule in moleculesToAdd.0 { for molecule in moleculesToAdd.0 {
@ -41,30 +44,30 @@ public extension AddMolecules {
} }
newMolecules.append(molecule) newMolecules.append(molecule)
} }
return (newMolecules, moleculesToAdd.1, moleculesToAdd.2) return (newMolecules, moleculesToAdd.1)
} }
} }
public extension RemoveMolecules { public extension RemoveMolecules {
/// Gets the molecules that need to be removed, recursively checking for additional molecules to remove. /// Convenience function that gets the molecules that need to be removed, recursively checking for additional molecules to remove.
func getRecursiveMoleculesToRemove() -> ([ListItemModelProtocol & MoleculeModelProtocol], animation: UITableView.RowAnimation?)? { func getRecursiveMoleculesToRemove() -> [ListItemModelProtocol & MoleculeModelProtocol]? {
guard let moleculesToRemove = moleculesToRemove() else { return nil } guard let moleculesToRemove = moleculesToRemove() else { return nil }
var newMolecules: [ListItemModelProtocol & MoleculeModelProtocol] = moleculesToRemove.0 var newMolecules: [ListItemModelProtocol & MoleculeModelProtocol] = moleculesToRemove
for case let molecule as (ListItemModelProtocol & MoleculeModelProtocol & RemoveMolecules) in moleculesToRemove.0 { for case let molecule as (ListItemModelProtocol & MoleculeModelProtocol & RemoveMolecules) in moleculesToRemove {
guard let moreMolecules = molecule.getRecursiveMoleculesToRemove() else { continue } guard let moreMolecules = molecule.getRecursiveMoleculesToRemove() else { continue }
newMolecules.append(contentsOf: moreMolecules.0) newMolecules.append(contentsOf: moreMolecules)
} }
return (newMolecules, moleculesToRemove.1) return newMolecules
} }
} }
public class AddRemoveMoleculeBehaviorModel: PageBehaviorModelProtocol { public class AddRemoveMoleculesBehaviorModel: PageBehaviorModelProtocol {
public class var identifier: String { "addRemoveListItemBehavior" } public class var identifier: String { "addRemoveListItemBehavior" }
public var shouldAllowMultipleInstances: Bool { false } public var shouldAllowMultipleInstances: Bool { false }
public init() {} public init() {}
} }
public class AddRemoveMoleculeBehavior: PageCustomActionHandlerBehavior { public class AddRemoveMoleculesBehavior: PageCustomActionHandlerBehavior, PageMoleculeTransformationBehavior {
var delegate: MVMCoreUIDelegateObject? var delegate: MVMCoreUIDelegateObject?
@ -72,22 +75,34 @@ public class AddRemoveMoleculeBehavior: PageCustomActionHandlerBehavior {
self.delegate = delegateObject self.delegate = delegateObject
} }
public func onPageNew(rootMolecules: [MoleculeModelProtocol], _ delegateObject: MVMCoreUIDelegateObject) {
guard let list = delegate?.moleculeListDelegate else { return }
for case let model as (MoleculeModelProtocol & ListItemModelProtocol & AddMolecules) in rootMolecules {
if let moleculesToAdd = model.getRecursiveMoleculesToAdd(),
let indexPath = list.getAdjustedIndexPath(for: model, position: moleculesToAdd.1) {
list.addMolecules(moleculesToAdd.0, indexPath: indexPath, animation: nil)
}
}
}
public func handleAction(type actionType: String?, information: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) -> Bool { public func handleAction(type actionType: String?, information: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) -> Bool {
if actionType == AddMoleculesActionModel.identifier { if actionType == AddMoleculesActionModel.identifier {
guard let list = delegate?.moleculeListDelegate, guard let list = delegate?.moleculeListDelegate,
let model = additionalData?[KeySourceModel] as? (ListItemModelProtocol & MoleculeModelProtocol & AddMolecules), let model = additionalData?[KeySourceModel] as? (ListItemModelProtocol & MoleculeModelProtocol & AddMolecules),
let moleculesToAdd = model.getRecursiveMoleculesToAdd(), let moleculesToAdd = model.getRecursiveMoleculesToAdd(),
let indexPath = list.getAdjustedIndexPath(for: model, position: moleculesToAdd.1) else { return true } let indexPath = list.getAdjustedIndexPath(for: model, position: moleculesToAdd.1),
let animation = information?["animation"] as? Int else { return true }
DispatchQueue.main.async { DispatchQueue.main.async {
list.addMolecules(moleculesToAdd.0, indexPath: indexPath, animation: moleculesToAdd.2) list.addMolecules(moleculesToAdd.0, indexPath: indexPath, animation: UITableView.RowAnimation(rawValue: animation))
} }
return true return true
} else if actionType == RemoveMoleculesActionModel.identifier { } else if actionType == RemoveMoleculesActionModel.identifier {
guard let list = delegate?.moleculeListDelegate, guard let list = delegate?.moleculeListDelegate,
let model = additionalData?[KeySourceModel] as? (ListItemModelProtocol & MoleculeModelProtocol & RemoveMolecules), let model = additionalData?[KeySourceModel] as? (ListItemModelProtocol & MoleculeModelProtocol & RemoveMolecules),
let moleculesToRemove = model.getRecursiveMoleculesToRemove() else { return true } let moleculesToRemove = model.getRecursiveMoleculesToRemove(),
let animation = information?["animation"] as? Int else { return true }
DispatchQueue.main.async { DispatchQueue.main.async {
list.removeMolecules(moleculesToRemove.0, animation: moleculesToRemove.1) list.removeMolecules(moleculesToRemove, animation: UITableView.RowAnimation(rawValue: animation))
} }
return true return true
} }
@ -97,7 +112,7 @@ public class AddRemoveMoleculeBehavior: PageCustomActionHandlerBehavior {
private extension MoleculeListProtocol { private extension MoleculeListProtocol {
/// Convenience function to get the index path adjusted for position. /// Convenience function to get the index path adjusted for position.
func getAdjustedIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol, position: AddMoleculePosition) -> IndexPath? { func getAdjustedIndexPath(for molecule: ListItemModelProtocol & MoleculeModelProtocol, position: AddMoleculesPosition) -> IndexPath? {
guard let indexPath = getIndexPath(for: molecule) else { return nil } guard let indexPath = getIndexPath(for: molecule) else { return nil }
if position == .below { if position == .below {
return IndexPath(row: indexPath.row + 1, section: indexPath.section) return IndexPath(row: indexPath.row + 1, section: indexPath.section)
@ -109,6 +124,7 @@ private extension MoleculeListProtocol {
public class AddMoleculesActionModel: ActionModelProtocol { public class AddMoleculesActionModel: ActionModelProtocol {
public static var identifier: String = "addMoleculesAction" public static var identifier: String = "addMoleculesAction"
public var actionType: String = AddMoleculesActionModel.identifier public var actionType: String = AddMoleculesActionModel.identifier
public var animation: UITableView.RowAnimation = .automatic
public var extraParameters: JSONValueDictionary? public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary? public var analyticsData: JSONValueDictionary?
@ -116,15 +132,26 @@ public class AddMoleculesActionModel: ActionModelProtocol {
// MARK: - Initializer // MARK: - Initializer
//-------------------------------------------------- //--------------------------------------------------
public init(_ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) { public init(_ animation: UITableView.RowAnimation, extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.animation = animation
self.extraParameters = extraParameters self.extraParameters = extraParameters
self.analyticsData = analyticsData self.analyticsData = analyticsData
} }
public required init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
if let animation = try typeContainer.decodeIfPresent(UITableView.RowAnimation.self, forKey: .animation) {
self.animation = animation
}
extraParameters = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .extraParameters)
analyticsData = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .analyticsData)
}
} }
public class RemoveMoleculesActionModel: ActionModelProtocol { public class RemoveMoleculesActionModel: ActionModelProtocol {
public static var identifier: String = "removeMoleculesAction" public static var identifier: String = "removeMoleculesAction"
public var actionType: String = RemoveMoleculesActionModel.identifier public var actionType: String = RemoveMoleculesActionModel.identifier
public var animation: UITableView.RowAnimation = .automatic
public var extraParameters: JSONValueDictionary? public var extraParameters: JSONValueDictionary?
public var analyticsData: JSONValueDictionary? public var analyticsData: JSONValueDictionary?
@ -132,8 +159,18 @@ public class RemoveMoleculesActionModel: ActionModelProtocol {
// MARK: - Initializer // MARK: - Initializer
//-------------------------------------------------- //--------------------------------------------------
public init(_ extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) { public init(_ animation: UITableView.RowAnimation, extraParameters: JSONValueDictionary? = nil, _ analyticsData: JSONValueDictionary? = nil) {
self.animation = animation
self.extraParameters = extraParameters self.extraParameters = extraParameters
self.analyticsData = analyticsData self.analyticsData = analyticsData
} }
public required init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
if let animation = try typeContainer.decodeIfPresent(UITableView.RowAnimation.self, forKey: .animation) {
self.animation = animation
}
extraParameters = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .extraParameters)
analyticsData = try typeContainer.decodeIfPresent(JSONValueDictionary.self, forKey: .analyticsData)
}
} }

View File

@ -222,7 +222,7 @@ open class CoreUIModelMapping: ModelMapping {
open class func registerBehaviors() { open class func registerBehaviors() {
ModelRegistry.register(handler: ScreenBrightnessModifierBehavior.self, for: ScreenBrightnessModifierBehaviorModel.self) ModelRegistry.register(handler: ScreenBrightnessModifierBehavior.self, for: ScreenBrightnessModifierBehaviorModel.self)
ModelRegistry.register(handler: PageGetContactBehavior.self, for: PageGetContactBehaviorModel.self) ModelRegistry.register(handler: PageGetContactBehavior.self, for: PageGetContactBehaviorModel.self)
ModelRegistry.register(handler: AddRemoveMoleculeBehavior.self, for: AddRemoveMoleculeBehaviorModel.self) ModelRegistry.register(handler: AddRemoveMoleculesBehavior.self, for: AddRemoveMoleculesBehaviorModel.self)
} }
open override class func registerActions() { open override class func registerActions() {