diff --git a/MVMCoreUI/Atoms/TextFields/ItemDropdownEntryField.swift b/MVMCoreUI/Atoms/TextFields/ItemDropdownEntryField.swift index d1728b46..48728ce9 100644 --- a/MVMCoreUI/Atoms/TextFields/ItemDropdownEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/ItemDropdownEntryField.swift @@ -26,6 +26,10 @@ open class ItemDropdownEntryField: BaseDropdownEntryField { /// Closure passed here will run upon dismissing the selection picker. public var observeDropdownSelection: ((String)->())? + public var itemDropdownEntryFieldModel: ItemDropdownEntryFieldModel? { + return model as? ItemDropdownEntryFieldModel + } + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -100,7 +104,7 @@ open class ItemDropdownEntryField: BaseDropdownEntryField { setPickerDelegates(delegate: self) if let pickerView = pickerView { - self.pickerView(pickerView, didSelectRow: 0, inComponent: 0) + self.pickerView(pickerView, didSelectRow: model.selectedIndex, inComponent: 0) } } } @@ -127,6 +131,7 @@ extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource { observeDropdownChange?(text ?? "", pickerData[row]) text = pickerData[row] + itemDropdownEntryFieldModel?.selectedIndex = row } } diff --git a/MVMCoreUI/Atoms/TextFields/ItemDropdownEntryFieldModel.swift b/MVMCoreUI/Atoms/TextFields/ItemDropdownEntryFieldModel.swift index 03c1cf40..56612b7b 100644 --- a/MVMCoreUI/Atoms/TextFields/ItemDropdownEntryFieldModel.swift +++ b/MVMCoreUI/Atoms/TextFields/ItemDropdownEntryFieldModel.swift @@ -16,6 +16,7 @@ } public var options: [String] = [] + public var selectedIndex: Int = 0 //-------------------------------------------------- // MARK: - Keys @@ -24,6 +25,7 @@ private enum CodingKeys: String, CodingKey { case moleculeName case options + case selectedIndex } //-------------------------------------------------- @@ -34,6 +36,7 @@ try super.init(from: decoder) let typeContainer = try decoder.container(keyedBy: CodingKeys.self) options = try typeContainer.decode([String].self, forKey: .options) + selectedIndex = try typeContainer.decodeIfPresent(Int.self, forKey: .selectedIndex) ?? 0 } public override func encode(to encoder: Encoder) throws { @@ -41,5 +44,6 @@ var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(options, forKey: .options) + try container.encode(options, forKey: .selectedIndex) } } diff --git a/MVMCoreUI/Atoms/TextFields/TextEntryField.swift b/MVMCoreUI/Atoms/TextFields/TextEntryField.swift index 66f3ecd4..57fdbb73 100644 --- a/MVMCoreUI/Atoms/TextFields/TextEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/TextEntryField.swift @@ -85,7 +85,10 @@ import UIKit /// The text of this TextField. open override var text: String? { get { return textField.text } - set { textField.text = newValue } + set { + textField.text = newValue + (model as? TextEntryFieldModel)?.text = newValue + } } /// Placeholder access for the TextField. diff --git a/MVMCoreUI/Atoms/Views/Label/Label.swift b/MVMCoreUI/Atoms/Views/Label/Label.swift index 208676df..576f66b9 100644 --- a/MVMCoreUI/Atoms/Views/Label/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label/Label.swift @@ -220,17 +220,27 @@ public typealias ActionBlock = () -> () } public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + clauses = [] - guard let labelModel = model as? LabelModel else { return } + + guard let labelModel = model as? LabelModel else { + text = "" + return + } + attributedText = nil + originalAttributedString = nil text = labelModel.text Label.setLabel(self, withHTML: labelModel.html) + let alignment = LabelAlignment(rawValue: labelModel.textAlignment ?? "") switch alignment { case .center: textAlignment = .center + case .right: textAlignment = .right + default: textAlignment = .left } @@ -239,9 +249,11 @@ public typealias ActionBlock = () -> () if let backgroundColor = labelModel.backgroundColor { self.backgroundColor = backgroundColor.uiColor } + if let accessibilityText = labelModel.accessibilityText { accessibilityLabel = accessibilityText } + if let fontStyle = labelModel.fontStyle { MFStyler.styleLabel(self, withStyle: fontStyle) MFStyler.styleLabel(self, withStyle: fontStyle, genericScaling: false) @@ -264,19 +276,23 @@ public typealias ActionBlock = () -> () if let attributes = labelModel.attributes, let labelText = text { let attributedString = NSMutableAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font.withSize(standardFontSize), NSAttributedString.Key.foregroundColor: textColor as UIColor]) + for attribute in attributes { let range = NSRange(location: attribute.location, length: attribute.length) switch attribute { case _ as LabelAttributeUnderlineModel: attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range) + case _ as LabelAttributeStrikeThroughModel: attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range) attributedString.addAttribute(.baselineOffset, value: 0, range: range) + case let colorAtt as LabelAttributeColorModel: if let colorHex = colorAtt.textColor, !colorHex.isEmpty { attributedString.removeAttribute(.foregroundColor, range: range) attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range) } + case let imageAtt as LabelAttributeImageModel: var fontSize = font.pointSize if let attributeSize = imageAtt.size { @@ -293,6 +309,7 @@ public typealias ActionBlock = () -> () let mutableString = NSMutableAttributedString() mutableString.append(NSAttributedString(attachment: imageAttachment)) attributedString.insert(mutableString, at: imageAtt.location) + case let fontAtt as LabelAttributeFontModel: if let fontStyle = fontAtt.style { let styles = MFStyler.styleGetAttributedString("0", withStyle: fontStyle) @@ -320,6 +337,7 @@ public typealias ActionBlock = () -> () } } addActionAttributes(range: range, string: attributedString) + default: continue } @@ -381,8 +399,9 @@ public typealias ActionBlock = () -> () } if let attributes = json?.optionalArrayForKey("attributes"), let labelText = label.text { - let attributedString = NSMutableAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: mvmLabel?.font.withSize(mvmLabel!.standardFontSize) ?? label.font as UIFont, - NSAttributedString.Key.foregroundColor: label.textColor as UIColor]) + let attributedString = NSMutableAttributedString(string: labelText, + attributes: [NSAttributedString.Key.font: mvmLabel?.font.withSize(mvmLabel!.standardFontSize) ?? label.font as UIFont, + NSAttributedString.Key.foregroundColor: label.textColor as UIColor]) for case let attribute as [String: Any] in attributes { guard let attributeType = attribute.optionalStringForKey(KeyType), let location = attribute["location"] as? Int, diff --git a/MVMCoreUI/Atoms/Views/Label/LabelModel.swift b/MVMCoreUI/Atoms/Views/Label/LabelModel.swift index b0ad442f..731b23da 100644 --- a/MVMCoreUI/Atoms/Views/Label/LabelModel.swift +++ b/MVMCoreUI/Atoms/Views/Label/LabelModel.swift @@ -48,19 +48,19 @@ import Foundation required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - self.moleculeName = try typeContainer.decodeIfPresent(String.self, forKey: .moleculeName) - self.text = try typeContainer.decode(String.self, forKey: .text) - self.accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) - self.textColor = try typeContainer.decodeIfPresent(String.self, forKey: .textColor) - self.backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) - self.fontStyle = try typeContainer.decodeIfPresent(String.self, forKey: .fontStyle) - self.fontName = try typeContainer.decodeIfPresent(String.self, forKey: .fontName) - self.fontSize = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .fontSize) - self.textAlignment = try typeContainer.decodeIfPresent(String.self, forKey: .textAlignment) - self.attributes = try typeContainer.decodeModelsIfPresent(codingKey: .attributes, typeCodingKey: AttributeTypeKey.type) as? [LabelAttributeModel] - self.html = try typeContainer.decodeIfPresent(String.self, forKey: .html) - self.hero = try typeContainer.decodeIfPresent(Int.self, forKey: .hero) - self.makeWholeViewClickable = try typeContainer.decodeIfPresent(Bool.self, forKey: .makeWholeViewClickable) + moleculeName = try typeContainer.decodeIfPresent(String.self, forKey: .moleculeName) + text = try typeContainer.decode(String.self, forKey: .text) + accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) + textColor = try typeContainer.decodeIfPresent(String.self, forKey: .textColor) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + fontStyle = try typeContainer.decodeIfPresent(String.self, forKey: .fontStyle) + fontName = try typeContainer.decodeIfPresent(String.self, forKey: .fontName) + fontSize = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .fontSize) + textAlignment = try typeContainer.decodeIfPresent(String.self, forKey: .textAlignment) + attributes = try typeContainer.decodeModelsIfPresent(codingKey: .attributes, typeCodingKey: AttributeTypeKey.type) as? [LabelAttributeModel] + html = try typeContainer.decodeIfPresent(String.self, forKey: .html) + hero = try typeContainer.decodeIfPresent(Int.self, forKey: .hero) + makeWholeViewClickable = try typeContainer.decodeIfPresent(Bool.self, forKey: .makeWholeViewClickable) } public func encode(to encoder: Encoder) throws { diff --git a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift index 5045d5a4..3176df92 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift @@ -68,9 +68,8 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { open override func updateViewConstraints() { super.updateViewConstraints() - guard let tableView = tableView else { - return - } + + guard let tableView = tableView else { return } let minimumSpace: CGFloat = minimumFillSpace() var currentSpace: CGFloat = 0 @@ -90,9 +89,8 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { totalMinimumSpace += minimumSpace } - guard fillTop || fillBottom else { - return - } + guard fillTop || fillBottom else { return } + let newSpace = MVMCoreUIUtility.getVariableConstraintHeight(currentSpace, in: tableView, minimumHeight: totalMinimumSpace) // If the bottom view is outside of the scroll, then only the top view constraint is being used, so we have to double it to account for the bottom constraint not being there when we compare to the new value. diff --git a/MVMCoreUI/Models/ModelProtocols/ListItemModelProtocol.swift b/MVMCoreUI/Models/ModelProtocols/ListItemModelProtocol.swift index 9d424a2b..c6be0488 100644 --- a/MVMCoreUI/Models/ModelProtocols/ListItemModelProtocol.swift +++ b/MVMCoreUI/Models/ModelProtocols/ListItemModelProtocol.swift @@ -17,19 +17,14 @@ public protocol ListItemModelProtocol: ContainerModelProtocol, MoleculeModelProt // Not a strict requirement. extension ListItemModelProtocol { + public var action: ActionModelProtocol? { - get { - return nil - } - set { - } + get { return nil } + set { } } public var style: String? { - get { - return nil - } - set { - } + get { return nil } + set { } } } diff --git a/MVMCoreUI/Molecules/Items/DropDownFilterTableViewCell.swift b/MVMCoreUI/Molecules/Items/DropDownFilterTableViewCell.swift index 8c813d24..877af234 100644 --- a/MVMCoreUI/Molecules/Items/DropDownFilterTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/DropDownFilterTableViewCell.swift @@ -26,6 +26,7 @@ import UIKit super.setupView() guard dropDown.superview == nil else { return } + addMolecule(dropDown) dropDown.observeDropdownChange = { [weak self] oldValue, newValue in @@ -40,7 +41,7 @@ import UIKit self.delegateObject?.moleculeDelegate?.removeMolecules(json2d[self.previousIndex], sender: self, animation: .fade) } - self.delegateObject?.moleculeDelegate?.addMolecules(json2d[index], sender: self, animation: .fade) + self.delegateObject?.moleculeDelegate?.addMolecules(json2d[index], sender: self, animation: .none) self.previousIndex = index } } diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 56ff1ca9..9f27a918 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -8,14 +8,19 @@ import UIKit + @objcMembers open class MoleculeTableViewCell: TableViewCell { // MARK: - MVMCoreUIMoleculeViewProtocol + public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.setWithModel(model, delegateObject, additionalData) + guard let model = model as? ListItemModel else { return } + if molecule != nil { (molecule as? ModelMoleculeViewProtocol)?.setWithModel(model.molecule, delegateObject, additionalData) + } else if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(model.molecule, delegateObject, false) { addMolecule(moleculeView) } @@ -24,28 +29,28 @@ import UIKit } public override class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { - guard let moleculeModel = (model as? ListItemModel)?.molecule else { - return "\(self)<>" - } + guard let moleculeModel = (model as? ListItemModel)?.molecule else { return "\(self)<>" } + let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moleculeModel) as? ModelMoleculeViewProtocol.Type let moleculeName = className?.nameForReuse(moleculeModel, delegateObject) ?? moleculeModel.moleculeName ?? "" + return "\(self)<\(moleculeName)>" } public class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), - let theClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON) else { - return nil - } + let theClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON) + else { return nil } + return theClass.requiredModules?(moleculeJSON, delegateObject: delegateObject, error: error) } public override class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { guard let moleculeModel = (molecule as? MoleculeContainerModel)?.molecule, let classType = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moleculeModel) as? ModelMoleculeViewProtocol.Type, - let height = classType.estimatedHeight(forRow: moleculeModel, delegateObject: delegateObject) else { - return 80 - } + let height = classType.estimatedHeight(forRow: moleculeModel, delegateObject: delegateObject) + else { return 80 } + return max(2 * PaddingDefaultVerticalSpacing3, height) } } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift index 3a270ad5..8226dfc1 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift @@ -24,9 +24,8 @@ open class HeadlineBody: View { // MARK: - Styling func style(with styleString: String?) { - guard let styleString = styleString else { - return - } + guard let styleString = styleString else { return } + switch styleString { case "header": stylePageHeader() @@ -72,10 +71,9 @@ open class HeadlineBody: View { open override func setupView() { super.setupView() - guard subviews.count == 0 else { - return - } - translatesAutoresizingMaskIntoConstraints = false + + guard subviews.isEmpty else { return } + backgroundColor = .clear clipsToBounds = true @@ -86,9 +84,9 @@ open class HeadlineBody: View { view.addSubview(headlineLabel) view.addSubview(messageLabel) - headlineLabel.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical) - messageLabel.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical) - view.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical) + headlineLabel.setContentHuggingPriority(.required, for: .vertical) + messageLabel.setContentHuggingPriority(.required, for: .vertical) + view.setContentHuggingPriority(.required, for: .vertical) headlineLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true @@ -110,7 +108,10 @@ open class HeadlineBody: View { view.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 0).isActive = true } + //-------------------------------------------------- // MARK: - Constraining + //-------------------------------------------------- + public func setSpacing() { if headlineLabel.hasText && messageLabel.hasText { spaceBetweenLabels?.constant = spaceBetweenLabelsConstant @@ -119,25 +120,32 @@ open class HeadlineBody: View { } } - // MARK:- ModelMoleculeViewProtocol + //-------------------------------------------------- + // MARK: - ModelMoleculeViewProtocol + //-------------------------------------------------- + public override class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return 58 } public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - super.setWithModel(model, delegateObject, additionalData) - guard let headlineBodyModel = model as? HeadlineBodyModel else { - return - } + + guard let headlineBodyModel = model as? HeadlineBodyModel else { return } style(with: headlineBodyModel.style) - + + print("Headline -H: \(headlineBodyModel.headline?.text ?? "~nada~")") + print("Headline -B: \(headlineBodyModel.body?.text ?? "~nada~")") + headlineLabel.setWithModel(headlineBodyModel.headline, delegateObject, additionalData) messageLabel.setWithModel(headlineBodyModel.body, delegateObject, additionalData) } + //-------------------------------------------------- // MARK: - MVMCoreUIMoleculeViewProtocol + //-------------------------------------------------- + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) @@ -159,4 +167,3 @@ open class HeadlineBody: View { return 58 } } - diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift index 62199654..1fbe98e7 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyModel.swift @@ -20,4 +20,3 @@ import Foundation self.headline = headline } } - diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 75ef7d2f..f9319927 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -9,52 +9,67 @@ import UIKit open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol { + //-------------------------------------------------- + // MARK: - Stored Properties + //-------------------------------------------------- public var moleculesInfo: [(identifier: String, class: AnyClass, molecule: ListItemModelProtocol)]? + var observer: NSKeyValueObservation? public var templateModel: ListPageTemplateModel? + + //-------------------------------------------------- + // MARK: - Computed Properties + //-------------------------------------------------- + @objc public override func parsePageJSON() throws { try parseTemplateJSON() } open override var loadObject: MVMCoreLoadObject? { didSet { - if loadObject != oldValue { - updateRequiredModules() - observer?.invalidate() - if let newObject = loadObject { - observer = newObject.observe(\MVMCoreLoadObject.pageJSON, options: [.old, .new]) { [weak self] (object, change) in - self?.updateRequiredModules() - } + guard loadObject != oldValue else { return } + + updateRequiredModules() + observer?.invalidate() + if let newObject = loadObject { + observer = newObject.observe(\MVMCoreLoadObject.pageJSON, options: [.old, .new]) { [weak self] object, change in + self?.updateRequiredModules() } } } } + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + open override func viewForTop() -> UIView { guard let headerModel = templateModel?.header, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { - return super.viewForTop() - } + let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, false) + else { return super.viewForTop() } // Temporary, Default the horizontal padding if var container = templateModel?.header as? ContainerModelProtocol, container.useHorizontalMargins == nil { container.useHorizontalMargins = true } + return molecule } override open func viewForBottom() -> UIView { guard let footerModel = templateModel?.footer, - let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(footerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { - return super.viewForBottom() - } + let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(footerModel, delegateObject() as? MVMCoreUIDelegateObject, false) + else { return super.viewForBottom() } + return molecule } open override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer) -> Bool { + guard super.shouldFinishProcessingLoad(loadObject, error: error) else { return false } + // This template requires atleast one of the three layers. if templateModel?.header == nil, templateModel?.molecules?.count ?? 0 == 0, @@ -72,7 +87,10 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol registerWithTable() } - // MARK: - table + //-------------------------------------------------- + // MARK: - Table View + //-------------------------------------------------- + open override func registerWithTable() { super.registerWithTable() guard let moleculesInfo = moleculesInfo else { return } @@ -84,9 +102,9 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol open override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { guard let moleculeInfo = moleculesInfo?[indexPath.row], - let estimatedHeight = (moleculeInfo.class as? ModelMoleculeViewProtocol.Type)?.estimatedHeight(forRow: moleculeInfo.molecule, delegateObject: delegateObject() as? MVMCoreUIDelegateObject) else { - return 0 - } + let estimatedHeight = (moleculeInfo.class as? ModelMoleculeViewProtocol.Type)?.estimatedHeight(forRow: moleculeInfo.molecule, delegateObject: delegateObject() as? MVMCoreUIDelegateObject) + else { return 0 } + return estimatedHeight } @@ -95,18 +113,22 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol } open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let moleculeInfo = moleculesInfo?[indexPath.row], - let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier) else { - return UITableViewCell() - } + let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier) + else { return UITableViewCell() } + let delegate = delegateObject() as? MVMCoreUIDelegateObject let moleculeCell = cell as? MVMCoreUIMoleculeViewProtocol moleculeCell?.reset?() + if let protocolCell = cell as? MoleculeListCellProtocol { protocolCell.setLines(with: templateModel?.line, delegateObject: delegate, additionalData: nil, indexPath: indexPath) } + (moleculeCell as? ModelMoleculeViewProtocol)?.setWithModel(moleculeInfo.molecule, delegate, nil) - moleculeCell?.updateView(tableView.bounds.width) + moleculeCell?.updateView(tableView.bounds.width) + return cell } @@ -118,16 +140,18 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol } open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + if let cell = tableView.cellForRow(at: indexPath) as? MoleculeListCellProtocol { cell.didSelectCell(at: indexPath, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil) } } - // MARK: - cache handling + //-------------------------------------------------- + // MARK: - Cache Handling + //-------------------------------------------------- + open override func pageTypesToListenFor() -> [Any]? { - guard let pageType = self.pageType else { - return super.pageTypesToListenFor() - } + guard let pageType = self.pageType else { return super.pageTypesToListenFor() } return [pageType] } @@ -135,8 +159,12 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol return loadObject?.requestParameters?.modules } + //-------------------------------------------------- // MARK: - MoleculeDelegateProtocol + //-------------------------------------------------- + open override func moleculeLayoutUpdated(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) { + if let tableView = tableView { let point = molecule.convert(molecule.bounds.origin, to: tableView) if let indexPath = tableView.indexPathForRow(at: point), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false { @@ -159,6 +187,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol DispatchQueue.main.async { guard let indexPath = self.tableView?.indexPath(for: sender) else { return } var indexPaths: [IndexPath] = [] + for molecule in tmpMolecules { if let info = self.getMoleculeInfo(with: molecule) { self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier) @@ -167,6 +196,9 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol indexPaths.append(IndexPath(row: index, section: 0)) } } + + print("ADD PATHS: \(indexPaths)") + self.tableView?.insertRows(at: indexPaths, with: animation) self.updateViewConstraints() self.view.layoutIfNeeded() @@ -184,25 +216,31 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol } var indexPaths: [IndexPath] = [] + //TODO: cehck for molecule protocola eqality + for molecule in tmpMolecules { - if let removeIndex = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in - return molecule.toJSONString() == moleculeInfo.molecule.toJSONString() - }) { + if let removeIndex = moleculesInfo?.firstIndex(where: { molecule.toJSON() == $0.molecule.toJSON() }) { + moleculesInfo?.remove(at: removeIndex) indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0)) } } - self.tableView?.deleteRows(at: indexPaths, with: animation) - self.updateViewConstraints() - self.view.layoutIfNeeded() + + print("REMOVE PATHS: \(indexPaths)") + + tableView?.deleteRows(at: indexPaths, with: animation) + updateViewConstraints() + view.layoutIfNeeded() } public func addMolecules(_ molecules: [ListItemModelProtocol], sender: UITableViewCell, 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 { guard let indexPath = self.tableView?.indexPath(for: sender) else { return } var indexPaths: [IndexPath] = [] + for molecule in molecules { if let info = self.getMoleculeInfo(with: molecule) { self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier) @@ -211,6 +249,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol indexPaths.append(IndexPath(row: index, section: 0)) } } + self.tableView?.insertRows(at: indexPaths, with: animation) self.updateViewConstraints() self.view.layoutIfNeeded() @@ -218,35 +257,42 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol } public func removeMolecules(_ molecules: [ListItemModelProtocol], sender: UITableViewCell, animation: UITableView.RowAnimation) { + var indexPaths: [IndexPath] = [] //TODO: cehck for molecule protocola eqality + for molecule in molecules { - if let removeIndex = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in - return molecule.toJSONString() == moleculeInfo.molecule.toJSONString() - }) { + if let removeIndex = moleculesInfo?.firstIndex(where: { molecule.toJSON() == $0.molecule.toJSON() }) { moleculesInfo?.remove(at: removeIndex) indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0)) } } - self.tableView?.deleteRows(at: indexPaths, with: animation) - self.updateViewConstraints() - self.view.layoutIfNeeded() + + tableView?.deleteRows(at: indexPaths, with: animation) + updateViewConstraints() + view.layoutIfNeeded() } + //-------------------------------------------------- // MARK: - Convenience + //-------------------------------------------------- + /// Returns the (identifier, class) of the molecule for the given map. func getMoleculeInfo(with listItem: ListItemModelProtocol?) -> (identifier: String, class: AnyClass, molecule: ListItemModelProtocol)? { + guard let listItem = listItem, let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(listItem), - let moleculeName = (moleculeClass as? ModelMoleculeViewProtocol.Type)?.nameForReuse(listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName else { - return nil - } + let moleculeName = (moleculeClass as? ModelMoleculeViewProtocol.Type)?.nameForReuse(listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName + else { return nil } + return (moleculeName, moleculeClass, listItem) } /// Sets up the molecule list and ensures no errors loading all content. func getMoleculeInfoList() -> [(identifier: String, class: AnyClass, molecule: ListItemModelProtocol)]? { + var moleculeList: [(identifier: String, class: AnyClass, molecule: ListItemModelProtocol)] = [] + if let molecules = templateModel?.molecules { for molecule in molecules { if let info = getMoleculeInfo(with: molecule) { @@ -254,6 +300,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol } } } + return moleculeList.count > 0 ? moleculeList : nil } @@ -271,15 +318,19 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol /// Gets modules required by the loadObject.pageJSON. open func requiredModules() -> [Any]? { + let modules: NSMutableArray = [] let delegate = delegateObject() as? MVMCoreUIDelegateObject + MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("header"), delegateObject: delegate, moduleList: modules, errorList: nil) MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("footer"), delegateObject: delegate, moduleList: modules, errorList: nil) + if let molecules = loadObject?.pageJSON?.optionalArrayForKey(KeyMolecules) as? [[AnyHashable: Any]] { for molecule in molecules { MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: molecule, delegateObject: delegate, moduleList: modules, errorList: nil) } } + return modules as? [Any] } }