// // MoleculeTableViewCell.swift // MVMCoreUI // // Created by Scott Pfeil on 4/18/19. // Copyright © 2019 Verizon Wireless. All rights reserved. // import UIKit @objcMembers open class MoleculeTableViewCell: UITableViewCell, MVMCoreUIMoleculeViewProtocol, MoleculeListCellProtocol { open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)? open var json: [AnyHashable: Any]? // For the accessory view convenience. public var caretView: CaretView? private var caretViewWidthSizeObject: MFSizeObject? private var caretViewHeightSizeObject: MFSizeObject? // For separation between cells. public var topSeparatorView: SeparatorView? public var bottomSeparatorView: SeparatorView? public enum SeparatorFrequency: String { case All = "all" case AllExceptTop = "allExceptTop" case AllExceptBottom = "allExceptBottom" case Between = "between" } // MARK: - Inits public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupView() } public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setupView() } // MARK: - MFViewProtocol public func updateView(_ size: CGFloat) { MFStyler.setDefaultMarginsFor(self, size: size, horizontal: true, vertical: true) if #available(iOS 11.0, *) { if accessoryView != nil { // Smaller left margin if accessory view. var margin = directionalLayoutMargins margin.trailing = 16 contentView.directionalLayoutMargins = margin } else { contentView.directionalLayoutMargins = directionalLayoutMargins } topSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading) bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading) } else { if accessoryView != nil { // Smaller left margin if accessory view. var margin = layoutMargins margin.right = 16 contentView.layoutMargins = margin } else { contentView.layoutMargins = layoutMargins } topSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left) bottomSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left) } molecule?.updateView(size) if let _ = accessoryView, let caretView = caretView, let widthObject = caretViewWidthSizeObject, let heightObject = caretViewHeightSizeObject { caretView.frame = CGRect(x: 0, y: 0, width: widthObject.getValueBased(onSize: size), height: heightObject.getValueBased(onSize: size)) } topSeparatorView?.updateView(size) bottomSeparatorView?.updateView(size) } public func setupView() { preservesSuperviewLayoutMargins = false contentView.preservesSuperviewLayoutMargins = false selectionStyle = .none } // MARK: - MVMCoreUIMoleculeViewProtocol public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { self.json = json; guard let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else { return } if molecule == nil { if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) { contentView.addSubview(moleculeView) var standardConstraints = true if let castView = moleculeView as? MVMCoreUIViewConstrainingProtocol { standardConstraints = castView.useStandardConstraints?() ?? true castView.shouldSetHorizontalMargins?(!standardConstraints) castView.shouldSetVerticalMargins?(!standardConstraints) } NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: standardConstraints).values)) if standardConstraints { let constraint = contentView.heightAnchor.constraint(equalToConstant: 80) constraint.priority = .defaultLow constraint.isActive = true } molecule = moleculeView } } else { molecule?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData) } backgroundColor = molecule?.backgroundColor // Add the caret if there is an action and it's not declared hidden. if let _ = json.optionalDictionaryForKey("actionMap"), !json.boolForKey("hideArrow") { addCaretViewAccessory() } else { accessoryView = nil } // override the separator if let separator = json.optionalDictionaryForKey("separator") { addSeparatorsIfNeeded() bottomSeparatorView?.setWithJSON(separator, delegateObject: delegateObject, additionalData: additionalData) } } public func reset() { molecule?.reset?() } public static func estimatedHeight(forRow json: [AnyHashable: Any]?) -> CGFloat { guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let theClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON, delegateObject: nil), let estimatedHeightFor = theClass.estimatedHeight else { return 80 } return estimatedHeightFor(moleculeJSON) } public static func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { if let molecule = molecule?.optionalDictionaryForKey(KeyMolecule), let moleculeName = molecule.optionalStringForKey(KeyMoleculeName), let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping[moleculeName] as? AnyClass, let castClass = moleculeClass as? MVMCoreUIMoleculeViewProtocol.Type, let nameFunc = castClass.name { return nameFunc(molecule, delegateObject) } else { return molecule?.optionalDictionaryForKey(KeyMolecule)?.optionalStringForKey(KeyMoleculeName) } } // MARK: - Arrow /// Adds the standard mvm style caret to the accessory view public func addCaretViewAccessory() { guard accessoryView == nil else { return } let width: CGFloat = 6 let height: CGFloat = 10 caretView = CaretView(lineThickness: CaretView.thin) caretView?.frame = CGRect(x: 0, y: 0, width: width, height: height) caretViewWidthSizeObject = MFSizeObject(standardSize: width, standardiPadPortraitSize: 9) caretViewHeightSizeObject = MFSizeObject(standardSize: height, standardiPadPortraitSize: 16) accessoryView = caretView } // MARK: - MoleculeListCellProtocol /// For when the separator between cells shows using json and frequency. Default is type: standard, frequency: allExceptTop. public func setSeparatorWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?, indexPath: IndexPath) { addSeparatorsIfNeeded() if let json = json { topSeparatorView?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) bottomSeparatorView?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) if let separatorFrequencyString = json.optionalStringForKey("frequency"), let separatorFrequency = SeparatorFrequency(rawValue: separatorFrequencyString) { setSeparatorFrequency(separatorFrequency, indexPath: indexPath) } } else { topSeparatorView?.hide() bottomSeparatorView?.setAsLight() setSeparatorFrequency(MoleculeTableViewCell.SeparatorFrequency.AllExceptTop, indexPath: indexPath) } } public func didSelectCell(atIndex indexPath: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { if let actionMap = json?.optionalDictionaryForKey("actionMap") { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } // MARK: - Separator func addSeparatorsIfNeeded() { if topSeparatorView == nil { topSeparatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionTop) topSeparatorView?.hide() } if bottomSeparatorView == nil { bottomSeparatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionBot) bottomSeparatorView?.hide() } } /// For when the separator between cells shows. public func setSeparatorFrequency(_ separatorFrequency: SeparatorFrequency, indexPath: IndexPath) { switch separatorFrequency { case .All: if indexPath.row == 0 { topSeparatorView?.show() } else { topSeparatorView?.hide() } bottomSeparatorView?.show() case .AllExceptBottom: topSeparatorView?.show() bottomSeparatorView?.hide() case .Between: if indexPath.row == 0 { topSeparatorView?.hide() } else { topSeparatorView?.show() } bottomSeparatorView?.hide() case .AllExceptTop: fallthrough default: topSeparatorView?.hide() bottomSeparatorView?.show() } } }