Bug fix and general shape-up

This commit is contained in:
Kevin G Christiano 2020-02-06 09:31:23 -05:00
parent 8d6e448817
commit 15bfb6b481
12 changed files with 189 additions and 102 deletions

View File

@ -26,6 +26,10 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
/// Closure passed here will run upon dismissing the selection picker. /// Closure passed here will run upon dismissing the selection picker.
public var observeDropdownSelection: ((String)->())? public var observeDropdownSelection: ((String)->())?
public var itemDropdownEntryFieldModel: ItemDropdownEntryFieldModel? {
return model as? ItemDropdownEntryFieldModel
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
@ -100,7 +104,7 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
setPickerDelegates(delegate: self) setPickerDelegates(delegate: self)
if let pickerView = pickerView { 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]) observeDropdownChange?(text ?? "", pickerData[row])
text = pickerData[row] text = pickerData[row]
itemDropdownEntryFieldModel?.selectedIndex = row
} }
} }

View File

@ -16,6 +16,7 @@
} }
public var options: [String] = [] public var options: [String] = []
public var selectedIndex: Int = 0
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Keys // MARK: - Keys
@ -24,6 +25,7 @@
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case options case options
case selectedIndex
} }
//-------------------------------------------------- //--------------------------------------------------
@ -34,6 +36,7 @@
try super.init(from: decoder) try super.init(from: decoder)
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
options = try typeContainer.decode([String].self, forKey: .options) 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 { public override func encode(to encoder: Encoder) throws {
@ -41,5 +44,6 @@
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(options, forKey: .options) try container.encode(options, forKey: .options)
try container.encode(options, forKey: .selectedIndex)
} }
} }

View File

@ -85,7 +85,10 @@ import UIKit
/// The text of this TextField. /// The text of this TextField.
open override var text: String? { open override var text: String? {
get { return textField.text } get { return textField.text }
set { textField.text = newValue } set {
textField.text = newValue
(model as? TextEntryFieldModel)?.text = newValue
}
} }
/// Placeholder access for the TextField. /// Placeholder access for the TextField.

View File

@ -220,17 +220,27 @@ public typealias ActionBlock = () -> ()
} }
public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
clauses = [] clauses = []
guard let labelModel = model as? LabelModel else { return }
guard let labelModel = model as? LabelModel else {
text = ""
return
}
attributedText = nil attributedText = nil
originalAttributedString = nil
text = labelModel.text text = labelModel.text
Label.setLabel(self, withHTML: labelModel.html) Label.setLabel(self, withHTML: labelModel.html)
let alignment = LabelAlignment(rawValue: labelModel.textAlignment ?? "") let alignment = LabelAlignment(rawValue: labelModel.textAlignment ?? "")
switch alignment { switch alignment {
case .center: case .center:
textAlignment = .center textAlignment = .center
case .right: case .right:
textAlignment = .right textAlignment = .right
default: default:
textAlignment = .left textAlignment = .left
} }
@ -239,9 +249,11 @@ public typealias ActionBlock = () -> ()
if let backgroundColor = labelModel.backgroundColor { if let backgroundColor = labelModel.backgroundColor {
self.backgroundColor = backgroundColor.uiColor self.backgroundColor = backgroundColor.uiColor
} }
if let accessibilityText = labelModel.accessibilityText { if let accessibilityText = labelModel.accessibilityText {
accessibilityLabel = accessibilityText accessibilityLabel = accessibilityText
} }
if let fontStyle = labelModel.fontStyle { if let fontStyle = labelModel.fontStyle {
MFStyler.styleLabel(self, withStyle: fontStyle) MFStyler.styleLabel(self, withStyle: fontStyle)
MFStyler.styleLabel(self, withStyle: fontStyle, genericScaling: false) MFStyler.styleLabel(self, withStyle: fontStyle, genericScaling: false)
@ -264,19 +276,23 @@ public typealias ActionBlock = () -> ()
if let attributes = labelModel.attributes, let labelText = text { 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]) let attributedString = NSMutableAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font.withSize(standardFontSize), NSAttributedString.Key.foregroundColor: textColor as UIColor])
for attribute in attributes { for attribute in attributes {
let range = NSRange(location: attribute.location, length: attribute.length) let range = NSRange(location: attribute.location, length: attribute.length)
switch attribute { switch attribute {
case _ as LabelAttributeUnderlineModel: case _ as LabelAttributeUnderlineModel:
attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range) attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range)
case _ as LabelAttributeStrikeThroughModel: case _ as LabelAttributeStrikeThroughModel:
attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range) attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range)
attributedString.addAttribute(.baselineOffset, value: 0, range: range) attributedString.addAttribute(.baselineOffset, value: 0, range: range)
case let colorAtt as LabelAttributeColorModel: case let colorAtt as LabelAttributeColorModel:
if let colorHex = colorAtt.textColor, !colorHex.isEmpty { if let colorHex = colorAtt.textColor, !colorHex.isEmpty {
attributedString.removeAttribute(.foregroundColor, range: range) attributedString.removeAttribute(.foregroundColor, range: range)
attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range) attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range)
} }
case let imageAtt as LabelAttributeImageModel: case let imageAtt as LabelAttributeImageModel:
var fontSize = font.pointSize var fontSize = font.pointSize
if let attributeSize = imageAtt.size { if let attributeSize = imageAtt.size {
@ -293,6 +309,7 @@ public typealias ActionBlock = () -> ()
let mutableString = NSMutableAttributedString() let mutableString = NSMutableAttributedString()
mutableString.append(NSAttributedString(attachment: imageAttachment)) mutableString.append(NSAttributedString(attachment: imageAttachment))
attributedString.insert(mutableString, at: imageAtt.location) attributedString.insert(mutableString, at: imageAtt.location)
case let fontAtt as LabelAttributeFontModel: case let fontAtt as LabelAttributeFontModel:
if let fontStyle = fontAtt.style { if let fontStyle = fontAtt.style {
let styles = MFStyler.styleGetAttributedString("0", withStyle: fontStyle) let styles = MFStyler.styleGetAttributedString("0", withStyle: fontStyle)
@ -320,6 +337,7 @@ public typealias ActionBlock = () -> ()
} }
} }
addActionAttributes(range: range, string: attributedString) addActionAttributes(range: range, string: attributedString)
default: default:
continue continue
} }
@ -381,8 +399,9 @@ public typealias ActionBlock = () -> ()
} }
if let attributes = json?.optionalArrayForKey("attributes"), let labelText = label.text { 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, let attributedString = NSMutableAttributedString(string: labelText,
NSAttributedString.Key.foregroundColor: label.textColor as UIColor]) 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 { for case let attribute as [String: Any] in attributes {
guard let attributeType = attribute.optionalStringForKey(KeyType), guard let attributeType = attribute.optionalStringForKey(KeyType),
let location = attribute["location"] as? Int, let location = attribute["location"] as? Int,

View File

@ -48,19 +48,19 @@ import Foundation
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
self.moleculeName = try typeContainer.decodeIfPresent(String.self, forKey: .moleculeName) moleculeName = try typeContainer.decodeIfPresent(String.self, forKey: .moleculeName)
self.text = try typeContainer.decode(String.self, forKey: .text) text = try typeContainer.decode(String.self, forKey: .text)
self.accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
self.textColor = try typeContainer.decodeIfPresent(String.self, forKey: .textColor) textColor = try typeContainer.decodeIfPresent(String.self, forKey: .textColor)
self.backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
self.fontStyle = try typeContainer.decodeIfPresent(String.self, forKey: .fontStyle) fontStyle = try typeContainer.decodeIfPresent(String.self, forKey: .fontStyle)
self.fontName = try typeContainer.decodeIfPresent(String.self, forKey: .fontName) fontName = try typeContainer.decodeIfPresent(String.self, forKey: .fontName)
self.fontSize = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .fontSize) fontSize = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .fontSize)
self.textAlignment = try typeContainer.decodeIfPresent(String.self, forKey: .textAlignment) textAlignment = try typeContainer.decodeIfPresent(String.self, forKey: .textAlignment)
self.attributes = try typeContainer.decodeModelsIfPresent(codingKey: .attributes, typeCodingKey: AttributeTypeKey.type) as? [LabelAttributeModel] attributes = try typeContainer.decodeModelsIfPresent(codingKey: .attributes, typeCodingKey: AttributeTypeKey.type) as? [LabelAttributeModel]
self.html = try typeContainer.decodeIfPresent(String.self, forKey: .html) html = try typeContainer.decodeIfPresent(String.self, forKey: .html)
self.hero = try typeContainer.decodeIfPresent(Int.self, forKey: .hero) hero = try typeContainer.decodeIfPresent(Int.self, forKey: .hero)
self.makeWholeViewClickable = try typeContainer.decodeIfPresent(Bool.self, forKey: .makeWholeViewClickable) makeWholeViewClickable = try typeContainer.decodeIfPresent(Bool.self, forKey: .makeWholeViewClickable)
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {

View File

@ -68,9 +68,8 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController {
open override func updateViewConstraints() { open override func updateViewConstraints() {
super.updateViewConstraints() super.updateViewConstraints()
guard let tableView = tableView else {
return guard let tableView = tableView else { return }
}
let minimumSpace: CGFloat = minimumFillSpace() let minimumSpace: CGFloat = minimumFillSpace()
var currentSpace: CGFloat = 0 var currentSpace: CGFloat = 0
@ -90,9 +89,8 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController {
totalMinimumSpace += minimumSpace totalMinimumSpace += minimumSpace
} }
guard fillTop || fillBottom else { guard fillTop || fillBottom else { return }
return
}
let newSpace = MVMCoreUIUtility.getVariableConstraintHeight(currentSpace, in: tableView, minimumHeight: totalMinimumSpace) 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. // 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.

View File

@ -17,19 +17,14 @@ public protocol ListItemModelProtocol: ContainerModelProtocol, MoleculeModelProt
// Not a strict requirement. // Not a strict requirement.
extension ListItemModelProtocol { extension ListItemModelProtocol {
public var action: ActionModelProtocol? { public var action: ActionModelProtocol? {
get { get { return nil }
return nil set { }
}
set {
}
} }
public var style: String? { public var style: String? {
get { get { return nil }
return nil set { }
}
set {
}
} }
} }

View File

@ -26,6 +26,7 @@ import UIKit
super.setupView() super.setupView()
guard dropDown.superview == nil else { return } guard dropDown.superview == nil else { return }
addMolecule(dropDown) addMolecule(dropDown)
dropDown.observeDropdownChange = { [weak self] oldValue, newValue in 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?.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 self.previousIndex = index
} }
} }

View File

@ -8,14 +8,19 @@
import UIKit import UIKit
@objcMembers open class MoleculeTableViewCell: TableViewCell { @objcMembers open class MoleculeTableViewCell: TableViewCell {
// MARK: - MVMCoreUIMoleculeViewProtocol // MARK: - MVMCoreUIMoleculeViewProtocol
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithModel(model, delegateObject, additionalData) super.setWithModel(model, delegateObject, additionalData)
guard let model = model as? ListItemModel else { return } guard let model = model as? ListItemModel else { return }
if molecule != nil { if molecule != nil {
(molecule as? ModelMoleculeViewProtocol)?.setWithModel(model.molecule, delegateObject, additionalData) (molecule as? ModelMoleculeViewProtocol)?.setWithModel(model.molecule, delegateObject, additionalData)
} else if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(model.molecule, delegateObject, false) { } else if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(model.molecule, delegateObject, false) {
addMolecule(moleculeView) addMolecule(moleculeView)
} }
@ -24,28 +29,28 @@ import UIKit
} }
public override class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { public override class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
guard let moleculeModel = (model as? ListItemModel)?.molecule else { guard let moleculeModel = (model as? ListItemModel)?.molecule else { return "\(self)<>" }
return "\(self)<>"
}
let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moleculeModel) as? ModelMoleculeViewProtocol.Type let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moleculeModel) as? ModelMoleculeViewProtocol.Type
let moleculeName = className?.nameForReuse(moleculeModel, delegateObject) ?? moleculeModel.moleculeName ?? "" let moleculeName = className?.nameForReuse(moleculeModel, delegateObject) ?? moleculeModel.moleculeName ?? ""
return "\(self)<\(moleculeName)>" return "\(self)<\(moleculeName)>"
} }
public class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? { public class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule),
let theClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON) else { let theClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)
return nil else { return nil }
}
return theClass.requiredModules?(moleculeJSON, delegateObject: delegateObject, error: error) return theClass.requiredModules?(moleculeJSON, delegateObject: delegateObject, error: error)
} }
public override class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { public override class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
guard let moleculeModel = (molecule as? MoleculeContainerModel)?.molecule, guard let moleculeModel = (molecule as? MoleculeContainerModel)?.molecule,
let classType = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moleculeModel) as? ModelMoleculeViewProtocol.Type, let classType = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(moleculeModel) as? ModelMoleculeViewProtocol.Type,
let height = classType.estimatedHeight(forRow: moleculeModel, delegateObject: delegateObject) else { let height = classType.estimatedHeight(forRow: moleculeModel, delegateObject: delegateObject)
return 80 else { return 80 }
}
return max(2 * PaddingDefaultVerticalSpacing3, height) return max(2 * PaddingDefaultVerticalSpacing3, height)
} }
} }

View File

@ -24,9 +24,8 @@ open class HeadlineBody: View {
// MARK: - Styling // MARK: - Styling
func style(with styleString: String?) { func style(with styleString: String?) {
guard let styleString = styleString else { guard let styleString = styleString else { return }
return
}
switch styleString { switch styleString {
case "header": case "header":
stylePageHeader() stylePageHeader()
@ -72,10 +71,9 @@ open class HeadlineBody: View {
open override func setupView() { open override func setupView() {
super.setupView() super.setupView()
guard subviews.count == 0 else {
return guard subviews.isEmpty else { return }
}
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .clear backgroundColor = .clear
clipsToBounds = true clipsToBounds = true
@ -86,9 +84,9 @@ open class HeadlineBody: View {
view.addSubview(headlineLabel) view.addSubview(headlineLabel)
view.addSubview(messageLabel) view.addSubview(messageLabel)
headlineLabel.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical) headlineLabel.setContentHuggingPriority(.required, for: .vertical)
messageLabel.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical) messageLabel.setContentHuggingPriority(.required, for: .vertical)
view.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical) view.setContentHuggingPriority(.required, for: .vertical)
headlineLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true 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 view.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 0).isActive = true
} }
//--------------------------------------------------
// MARK: - Constraining // MARK: - Constraining
//--------------------------------------------------
public func setSpacing() { public func setSpacing() {
if headlineLabel.hasText && messageLabel.hasText { if headlineLabel.hasText && messageLabel.hasText {
spaceBetweenLabels?.constant = spaceBetweenLabelsConstant 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? { public override class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return 58 return 58
} }
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithModel(model, delegateObject, additionalData) 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) style(with: headlineBodyModel.style)
print("Headline -H: \(headlineBodyModel.headline?.text ?? "~nada~")")
print("Headline -B: \(headlineBodyModel.body?.text ?? "~nada~")")
headlineLabel.setWithModel(headlineBodyModel.headline, delegateObject, additionalData) headlineLabel.setWithModel(headlineBodyModel.headline, delegateObject, additionalData)
messageLabel.setWithModel(headlineBodyModel.body, delegateObject, additionalData) messageLabel.setWithModel(headlineBodyModel.body, delegateObject, additionalData)
} }
//--------------------------------------------------
// MARK: - MVMCoreUIMoleculeViewProtocol // MARK: - MVMCoreUIMoleculeViewProtocol
//--------------------------------------------------
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
@ -159,4 +167,3 @@ open class HeadlineBody: View {
return 58 return 58
} }
} }

View File

@ -20,4 +20,3 @@ import Foundation
self.headline = headline self.headline = headline
} }
} }

View File

@ -9,52 +9,67 @@
import UIKit import UIKit
open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol { open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol {
//--------------------------------------------------
// MARK: - Stored Properties
//--------------------------------------------------
public var moleculesInfo: [(identifier: String, class: AnyClass, molecule: ListItemModelProtocol)]? public var moleculesInfo: [(identifier: String, class: AnyClass, molecule: ListItemModelProtocol)]?
var observer: NSKeyValueObservation? var observer: NSKeyValueObservation?
public var templateModel: ListPageTemplateModel? public var templateModel: ListPageTemplateModel?
//--------------------------------------------------
// MARK: - Computed Properties
//--------------------------------------------------
@objc public override func parsePageJSON() throws { @objc public override func parsePageJSON() throws {
try parseTemplateJSON() try parseTemplateJSON()
} }
open override var loadObject: MVMCoreLoadObject? { open override var loadObject: MVMCoreLoadObject? {
didSet { didSet {
if loadObject != oldValue { guard loadObject != oldValue else { return }
updateRequiredModules()
observer?.invalidate() updateRequiredModules()
if let newObject = loadObject { observer?.invalidate()
observer = newObject.observe(\MVMCoreLoadObject.pageJSON, options: [.old, .new]) { [weak self] (object, change) in if let newObject = loadObject {
self?.updateRequiredModules() observer = newObject.observe(\MVMCoreLoadObject.pageJSON, options: [.old, .new]) { [weak self] object, change in
} self?.updateRequiredModules()
} }
} }
} }
} }
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
open override func viewForTop() -> UIView { open override func viewForTop() -> UIView {
guard let headerModel = templateModel?.header, guard let headerModel = templateModel?.header,
let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(headerModel, delegateObject() as? MVMCoreUIDelegateObject, false)
return super.viewForTop() else { return super.viewForTop() }
}
// Temporary, Default the horizontal padding // Temporary, Default the horizontal padding
if var container = templateModel?.header as? ContainerModelProtocol, container.useHorizontalMargins == nil { if var container = templateModel?.header as? ContainerModelProtocol, container.useHorizontalMargins == nil {
container.useHorizontalMargins = true container.useHorizontalMargins = true
} }
return molecule return molecule
} }
override open func viewForBottom() -> UIView { override open func viewForBottom() -> UIView {
guard let footerModel = templateModel?.footer, guard let footerModel = templateModel?.footer,
let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(footerModel, delegateObject() as? MVMCoreUIDelegateObject, false) else { let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(footerModel, delegateObject() as? MVMCoreUIDelegateObject, false)
return super.viewForBottom() else { return super.viewForBottom() }
}
return molecule return molecule
} }
open override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject>) -> Bool { open override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject>) -> Bool {
guard super.shouldFinishProcessingLoad(loadObject, error: error) else { return false } guard super.shouldFinishProcessingLoad(loadObject, error: error) else { return false }
// This template requires atleast one of the three layers. // This template requires atleast one of the three layers.
if templateModel?.header == nil, if templateModel?.header == nil,
templateModel?.molecules?.count ?? 0 == 0, templateModel?.molecules?.count ?? 0 == 0,
@ -72,7 +87,10 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
registerWithTable() registerWithTable()
} }
// MARK: - table //--------------------------------------------------
// MARK: - Table View
//--------------------------------------------------
open override func registerWithTable() { open override func registerWithTable() {
super.registerWithTable() super.registerWithTable()
guard let moleculesInfo = moleculesInfo else { return } 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 { open override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
guard let moleculeInfo = moleculesInfo?[indexPath.row], guard let moleculeInfo = moleculesInfo?[indexPath.row],
let estimatedHeight = (moleculeInfo.class as? ModelMoleculeViewProtocol.Type)?.estimatedHeight(forRow: moleculeInfo.molecule, delegateObject: delegateObject() as? MVMCoreUIDelegateObject) else { let estimatedHeight = (moleculeInfo.class as? ModelMoleculeViewProtocol.Type)?.estimatedHeight(forRow: moleculeInfo.molecule, delegateObject: delegateObject() as? MVMCoreUIDelegateObject)
return 0 else { return 0 }
}
return estimatedHeight return estimatedHeight
} }
@ -95,18 +113,22 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
} }
open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let moleculeInfo = moleculesInfo?[indexPath.row], guard let moleculeInfo = moleculesInfo?[indexPath.row],
let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier) else { let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier)
return UITableViewCell() else { return UITableViewCell() }
}
let delegate = delegateObject() as? MVMCoreUIDelegateObject let delegate = delegateObject() as? MVMCoreUIDelegateObject
let moleculeCell = cell as? MVMCoreUIMoleculeViewProtocol let moleculeCell = cell as? MVMCoreUIMoleculeViewProtocol
moleculeCell?.reset?() moleculeCell?.reset?()
if let protocolCell = cell as? MoleculeListCellProtocol { if let protocolCell = cell as? MoleculeListCellProtocol {
protocolCell.setLines(with: templateModel?.line, delegateObject: delegate, additionalData: nil, indexPath: indexPath) protocolCell.setLines(with: templateModel?.line, delegateObject: delegate, additionalData: nil, indexPath: indexPath)
} }
(moleculeCell as? ModelMoleculeViewProtocol)?.setWithModel(moleculeInfo.molecule, delegate, nil) (moleculeCell as? ModelMoleculeViewProtocol)?.setWithModel(moleculeInfo.molecule, delegate, nil)
moleculeCell?.updateView(tableView.bounds.width) moleculeCell?.updateView(tableView.bounds.width)
return cell return cell
} }
@ -118,16 +140,18 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
} }
open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? MoleculeListCellProtocol { if let cell = tableView.cellForRow(at: indexPath) as? MoleculeListCellProtocol {
cell.didSelectCell(at: indexPath, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil) cell.didSelectCell(at: indexPath, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil)
} }
} }
// MARK: - cache handling //--------------------------------------------------
// MARK: - Cache Handling
//--------------------------------------------------
open override func pageTypesToListenFor() -> [Any]? { open override func pageTypesToListenFor() -> [Any]? {
guard let pageType = self.pageType else { guard let pageType = self.pageType else { return super.pageTypesToListenFor() }
return super.pageTypesToListenFor()
}
return [pageType] return [pageType]
} }
@ -135,8 +159,12 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
return loadObject?.requestParameters?.modules return loadObject?.requestParameters?.modules
} }
//--------------------------------------------------
// MARK: - MoleculeDelegateProtocol // MARK: - MoleculeDelegateProtocol
//--------------------------------------------------
open override func moleculeLayoutUpdated(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) { open override func moleculeLayoutUpdated(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) {
if let tableView = tableView { if let tableView = tableView {
let point = molecule.convert(molecule.bounds.origin, to: tableView) let point = molecule.convert(molecule.bounds.origin, to: tableView)
if let indexPath = tableView.indexPathForRow(at: point), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false { if let indexPath = tableView.indexPathForRow(at: point), tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false {
@ -159,6 +187,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
DispatchQueue.main.async { DispatchQueue.main.async {
guard let indexPath = self.tableView?.indexPath(for: sender) else { return } guard let indexPath = self.tableView?.indexPath(for: sender) else { return }
var indexPaths: [IndexPath] = [] var indexPaths: [IndexPath] = []
for molecule in tmpMolecules { for molecule in tmpMolecules {
if let info = self.getMoleculeInfo(with: molecule) { if let info = self.getMoleculeInfo(with: molecule) {
self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier) self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier)
@ -167,6 +196,9 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
indexPaths.append(IndexPath(row: index, section: 0)) indexPaths.append(IndexPath(row: index, section: 0))
} }
} }
print("ADD PATHS: \(indexPaths)")
self.tableView?.insertRows(at: indexPaths, with: animation) self.tableView?.insertRows(at: indexPaths, with: animation)
self.updateViewConstraints() self.updateViewConstraints()
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
@ -184,25 +216,31 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
} }
var indexPaths: [IndexPath] = [] var indexPaths: [IndexPath] = []
//TODO: cehck for molecule protocola eqality //TODO: cehck for molecule protocola eqality
for molecule in tmpMolecules { for molecule in tmpMolecules {
if let removeIndex = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in if let removeIndex = moleculesInfo?.firstIndex(where: { molecule.toJSON() == $0.molecule.toJSON() }) {
return molecule.toJSONString() == moleculeInfo.molecule.toJSONString()
}) {
moleculesInfo?.remove(at: removeIndex) moleculesInfo?.remove(at: removeIndex)
indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0)) indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0))
} }
} }
self.tableView?.deleteRows(at: indexPaths, with: animation)
self.updateViewConstraints() print("REMOVE PATHS: \(indexPaths)")
self.view.layoutIfNeeded()
tableView?.deleteRows(at: indexPaths, with: animation)
updateViewConstraints()
view.layoutIfNeeded()
} }
public func addMolecules(_ molecules: [ListItemModelProtocol], sender: UITableViewCell, animation: UITableView.RowAnimation) { 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. // This dispatch is needed to fix a race condition that can occur if this function is called during the table setup.
DispatchQueue.main.async { DispatchQueue.main.async {
guard let indexPath = self.tableView?.indexPath(for: sender) else { return } guard let indexPath = self.tableView?.indexPath(for: sender) else { return }
var indexPaths: [IndexPath] = [] var indexPaths: [IndexPath] = []
for molecule in molecules { for molecule in molecules {
if let info = self.getMoleculeInfo(with: molecule) { if let info = self.getMoleculeInfo(with: molecule) {
self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier) self.tableView?.register(info.class, forCellReuseIdentifier: info.identifier)
@ -211,6 +249,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
indexPaths.append(IndexPath(row: index, section: 0)) indexPaths.append(IndexPath(row: index, section: 0))
} }
} }
self.tableView?.insertRows(at: indexPaths, with: animation) self.tableView?.insertRows(at: indexPaths, with: animation)
self.updateViewConstraints() self.updateViewConstraints()
self.view.layoutIfNeeded() self.view.layoutIfNeeded()
@ -218,35 +257,42 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
} }
public func removeMolecules(_ molecules: [ListItemModelProtocol], sender: UITableViewCell, animation: UITableView.RowAnimation) { public func removeMolecules(_ molecules: [ListItemModelProtocol], sender: UITableViewCell, animation: UITableView.RowAnimation) {
var indexPaths: [IndexPath] = [] var indexPaths: [IndexPath] = []
//TODO: cehck for molecule protocola eqality //TODO: cehck for molecule protocola eqality
for molecule in molecules { for molecule in molecules {
if let removeIndex = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in if let removeIndex = moleculesInfo?.firstIndex(where: { molecule.toJSON() == $0.molecule.toJSON() }) {
return molecule.toJSONString() == moleculeInfo.molecule.toJSONString()
}) {
moleculesInfo?.remove(at: removeIndex) moleculesInfo?.remove(at: removeIndex)
indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0)) indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0))
} }
} }
self.tableView?.deleteRows(at: indexPaths, with: animation)
self.updateViewConstraints() tableView?.deleteRows(at: indexPaths, with: animation)
self.view.layoutIfNeeded() updateViewConstraints()
view.layoutIfNeeded()
} }
//--------------------------------------------------
// MARK: - Convenience // MARK: - Convenience
//--------------------------------------------------
/// Returns the (identifier, class) of the molecule for the given map. /// Returns the (identifier, class) of the molecule for the given map.
func getMoleculeInfo(with listItem: ListItemModelProtocol?) -> (identifier: String, class: AnyClass, molecule: ListItemModelProtocol)? { func getMoleculeInfo(with listItem: ListItemModelProtocol?) -> (identifier: String, class: AnyClass, molecule: ListItemModelProtocol)? {
guard let listItem = listItem, guard let listItem = listItem,
let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(listItem), let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(listItem),
let moleculeName = (moleculeClass as? ModelMoleculeViewProtocol.Type)?.nameForReuse(listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName else { let moleculeName = (moleculeClass as? ModelMoleculeViewProtocol.Type)?.nameForReuse(listItem, delegateObject() as? MVMCoreUIDelegateObject) ?? listItem.moleculeName
return nil else { return nil }
}
return (moleculeName, moleculeClass, listItem) return (moleculeName, moleculeClass, listItem)
} }
/// Sets up the molecule list and ensures no errors loading all content. /// Sets up the molecule list and ensures no errors loading all content.
func getMoleculeInfoList() -> [(identifier: String, class: AnyClass, molecule: ListItemModelProtocol)]? { func getMoleculeInfoList() -> [(identifier: String, class: AnyClass, molecule: ListItemModelProtocol)]? {
var moleculeList: [(identifier: String, class: AnyClass, molecule: ListItemModelProtocol)] = [] var moleculeList: [(identifier: String, class: AnyClass, molecule: ListItemModelProtocol)] = []
if let molecules = templateModel?.molecules { if let molecules = templateModel?.molecules {
for molecule in molecules { for molecule in molecules {
if let info = getMoleculeInfo(with: molecule) { if let info = getMoleculeInfo(with: molecule) {
@ -254,6 +300,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
} }
} }
} }
return moleculeList.count > 0 ? moleculeList : nil return moleculeList.count > 0 ? moleculeList : nil
} }
@ -271,15 +318,19 @@ open class MoleculeListTemplate: ThreeLayerTableViewController, TemplateProtocol
/// Gets modules required by the loadObject.pageJSON. /// Gets modules required by the loadObject.pageJSON.
open func requiredModules() -> [Any]? { open func requiredModules() -> [Any]? {
let modules: NSMutableArray = [] let modules: NSMutableArray = []
let delegate = delegateObject() as? MVMCoreUIDelegateObject let delegate = delegateObject() as? MVMCoreUIDelegateObject
MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("header"), delegateObject: delegate, moduleList: modules, errorList: nil) 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) MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("footer"), delegateObject: delegate, moduleList: modules, errorList: nil)
if let molecules = loadObject?.pageJSON?.optionalArrayForKey(KeyMolecules) as? [[AnyHashable: Any]] { if let molecules = loadObject?.pageJSON?.optionalArrayForKey(KeyMolecules) as? [[AnyHashable: Any]] {
for molecule in molecules { for molecule in molecules {
MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: molecule, delegateObject: delegate, moduleList: modules, errorList: nil) MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: molecule, delegateObject: delegate, moduleList: modules, errorList: nil)
} }
} }
return modules as? [Any] return modules as? [Any]
} }
} }