list item separation
This commit is contained in:
parent
48a940f160
commit
d4cacec310
@ -185,6 +185,7 @@
|
||||
D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */; };
|
||||
D2B18B7F2360913400A9AEDC /* Control.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B7E2360913400A9AEDC /* Control.swift */; };
|
||||
D2B18B812360945C00A9AEDC /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B802360945C00A9AEDC /* View.swift */; };
|
||||
D2B18B9A236883BA00A9AEDC /* TableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B18B99236883BA00A9AEDC /* TableViewCell.swift */; };
|
||||
D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B1E3E422F37D6A0065F95C /* ImageHeadlineBody.swift */; };
|
||||
D2C5001821F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h in Headers */ = {isa = PBXBuildFile; fileRef = D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D2C5001921F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */; };
|
||||
@ -383,6 +384,7 @@
|
||||
D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
D2B18B7E2360913400A9AEDC /* Control.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Control.swift; sourceTree = "<group>"; };
|
||||
D2B18B802360945C00A9AEDC /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = "<group>"; };
|
||||
\ D2B18B99236883BA00A9AEDC /* TableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewCell.swift; sourceTree = "<group>"; };
|
||||
D2B1E3E422F37D6A0065F95C /* ImageHeadlineBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageHeadlineBody.swift; sourceTree = "<group>"; };
|
||||
D2C5001621F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewControllerMappingObject.h; sourceTree = "<group>"; };
|
||||
D2C5001721F8ECDD001DA659 /* MVMCoreUIViewControllerMappingObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIViewControllerMappingObject.m; sourceTree = "<group>"; };
|
||||
@ -473,6 +475,7 @@
|
||||
D22479912316A9EF003FCCF9 /* Items */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D2B18B99236883BA00A9AEDC /* TableViewCell.swift */,
|
||||
01509D8E2327EC6F00EF99AA /* MoleculeTableViewCell.swift */,
|
||||
D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */,
|
||||
D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */,
|
||||
@ -570,7 +573,6 @@
|
||||
D29DF10E21E67A77003B2FB9 /* Molecules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */,
|
||||
D22479912316A9EF003FCCF9 /* Items */,
|
||||
D224798F2316A99F003FCCF9 /* LeftRightViews */,
|
||||
D224798E2316A995003FCCF9 /* HorizontalCombinationViews */,
|
||||
@ -586,6 +588,7 @@
|
||||
D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */,
|
||||
D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */,
|
||||
0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */,
|
||||
017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */,
|
||||
);
|
||||
path = Molecules;
|
||||
sourceTree = "<group>";
|
||||
@ -1104,6 +1107,7 @@
|
||||
D2A6390122CBB1820052ED1F /* Carousel.swift in Sources */,
|
||||
D29DF2C721E7BF57003B2FB9 /* MFTabBarInteractor.m in Sources */,
|
||||
D29DF29521E7ADB8003B2FB9 /* ProgrammaticScrollViewController.m in Sources */,
|
||||
D2B18B9A236883BA00A9AEDC /* TableViewCell.swift in Sources */,
|
||||
D2A638FD22CA98280052ED1F /* HeadlineBody.swift in Sources */,
|
||||
D29DF16121E69996003B2FB9 /* MFViewController.m in Sources */,
|
||||
D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */,
|
||||
|
||||
@ -115,7 +115,7 @@ open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeVi
|
||||
backgroundColor = .white
|
||||
}
|
||||
|
||||
public static func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
public class func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
guard let molecule = molecule?.optionalDictionaryForKey(KeyMolecule) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -8,227 +8,24 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers open class MoleculeTableViewCell: UITableViewCell, MVMCoreUIMoleculeViewProtocol, MoleculeListCellProtocol {
|
||||
|
||||
open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)?
|
||||
open var json: [AnyHashable: Any]?
|
||||
|
||||
// In updateView, will set padding to default.
|
||||
open var updateViewHorizontalDefaults = true
|
||||
|
||||
// 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"
|
||||
}
|
||||
|
||||
/// For subclasses that want to use a custom accessory view.
|
||||
open var customAccessoryView = false
|
||||
|
||||
public var topMarginPadding: CGFloat = 24
|
||||
public var bottomMarginPadding: CGFloat = 24
|
||||
|
||||
// MARK: - Styling
|
||||
func style(with styleString: String?) {
|
||||
guard let styleString = styleString else {
|
||||
return
|
||||
}
|
||||
switch styleString {
|
||||
case "standard":
|
||||
styleStandard()
|
||||
case "header":
|
||||
styleHeader()
|
||||
case "none":
|
||||
styleNone()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
// Ensures accessory view aligns to the center y derived from the
|
||||
if let center = heroAccessoryCenter {
|
||||
accessoryView?.center.y = center.y
|
||||
}
|
||||
}
|
||||
|
||||
var heroAccessoryCenter: CGPoint?
|
||||
|
||||
func styleStandard() {
|
||||
topMarginPadding = 24
|
||||
bottomMarginPadding = 24
|
||||
bottomSeparatorView?.show()
|
||||
bottomSeparatorView?.setAsLight()
|
||||
}
|
||||
|
||||
func styleHeader() {
|
||||
topMarginPadding = 48
|
||||
bottomMarginPadding = 16
|
||||
bottomSeparatorView?.show()
|
||||
bottomSeparatorView?.setAsRegular()
|
||||
}
|
||||
|
||||
func styleNone() {
|
||||
topMarginPadding = 0
|
||||
bottomMarginPadding = 0
|
||||
bottomSeparatorView?.hide()
|
||||
}
|
||||
|
||||
public func willDisplay() {
|
||||
|
||||
alignAccessoryToHero()
|
||||
}
|
||||
|
||||
// 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.setMarginsFor(self, size: size, defaultHorizontal: updateViewHorizontalDefaults, top: topMarginPadding, bottom: bottomMarginPadding)
|
||||
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)
|
||||
|
||||
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() {
|
||||
selectionStyle = .none
|
||||
insetsLayoutMarginsFromSafeArea = false
|
||||
contentView.insetsLayoutMarginsFromSafeArea = false
|
||||
contentView.preservesSuperviewLayoutMargins = false
|
||||
}
|
||||
|
||||
/// NOTE: Should only be called when displayed or about to be displayed.
|
||||
public func alignAccessoryToHero() {
|
||||
|
||||
// Layout call required to force draw in memory to get dimensions of subviews.
|
||||
layoutIfNeeded()
|
||||
guard let heroLabel = findHeroLabel(views: contentView.subviews), let hero = heroLabel.hero else { return }
|
||||
let rect = Label.boundingRect(forCharacterRange: NSRange(location: hero, length: 1), in: heroLabel)
|
||||
accessoryView?.center.y = contentView.convert(UIView(frame: rect).center, from: heroLabel).y
|
||||
heroAccessoryCenter = accessoryView?.center
|
||||
}
|
||||
|
||||
/// Traverses the view hierarchy for a 🦸♂️heroic Label.
|
||||
private func findHeroLabel(views: [UIView]) -> Label? {
|
||||
|
||||
if views.isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
var queue = [UIView]()
|
||||
|
||||
for view in views {
|
||||
// Only one Label will have a hero in a table cell.
|
||||
if let label = view as? Label, label.hero != nil {
|
||||
return label
|
||||
}
|
||||
queue.append(contentsOf: view.subviews)
|
||||
}
|
||||
|
||||
return findHeroLabel(views: queue)
|
||||
}
|
||||
@objcMembers open class MoleculeTableViewCell: TableViewCell {
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
self.json = json
|
||||
public override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
|
||||
style(with: json?.optionalStringForKey("style"))
|
||||
|
||||
if let useHorizontalMargins = json?.optionalBoolForKey("useHorizontalMargins") {
|
||||
updateViewHorizontalDefaults = useHorizontalMargins
|
||||
}
|
||||
|
||||
if (json?.optionalBoolForKey("useVerticalMargins") ?? true) == false {
|
||||
topMarginPadding = 0
|
||||
bottomMarginPadding = 0
|
||||
}
|
||||
|
||||
if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) {
|
||||
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
|
||||
}
|
||||
|
||||
// Add the caret if there is an action and it's not declared hidden.
|
||||
if !customAccessoryView {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
let standardConstraints = (moleculeView as? MVMCoreUIViewConstrainingProtocol)?.useStandardConstraints?() ?? true
|
||||
NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: standardConstraints).values))
|
||||
molecule = moleculeView
|
||||
}
|
||||
} else {
|
||||
molecule?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData)
|
||||
}
|
||||
|
||||
// This molecule will by default handle margins.
|
||||
if let castView = molecule as? MVMCoreUIViewConstrainingProtocol {
|
||||
castView.shouldSetHorizontalMargins?(false)
|
||||
castView.shouldSetVerticalMargins?(false)
|
||||
}
|
||||
guard molecule == nil, let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule), let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) else { return }
|
||||
addMolecule(moleculeView)
|
||||
}
|
||||
|
||||
public func reset() {
|
||||
molecule?.reset?()
|
||||
updateViewHorizontalDefaults = true
|
||||
styleStandard()
|
||||
backgroundColor = .white
|
||||
}
|
||||
|
||||
public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
|
||||
public override class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
|
||||
guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) else {
|
||||
return 80
|
||||
}
|
||||
return max(2 * PaddingDefaultVerticalSpacing3, height)
|
||||
}
|
||||
|
||||
public static func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
public override class func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
guard let molecule = molecule?.optionalDictionaryForKey(KeyMolecule) else {
|
||||
return "\(self)<>"
|
||||
}
|
||||
@ -236,87 +33,11 @@ import UIKit
|
||||
return "\(self)<\(moleculeName)>"
|
||||
}
|
||||
|
||||
public static 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),
|
||||
let theClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON) else {
|
||||
return nil
|
||||
}
|
||||
return theClass.requiredModules?(moleculeJSON, delegateObject: delegateObject, error: error)
|
||||
}
|
||||
|
||||
// MARK: - Arrow
|
||||
/// Adds the standard mvm style caret to the accessory view
|
||||
@objc 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
312
MVMCoreUI/Molecules/Items/TableViewCell.swift
Normal file
312
MVMCoreUI/Molecules/Items/TableViewCell.swift
Normal file
@ -0,0 +1,312 @@
|
||||
//
|
||||
// TableViewCell.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 10/29/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers open class TableViewCell: UITableViewCell {
|
||||
open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)?
|
||||
open var json: [AnyHashable: Any]?
|
||||
|
||||
// In updateView, will set padding to default.
|
||||
open var updateViewHorizontalDefaults = true
|
||||
|
||||
// For the accessory view convenience.
|
||||
private 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"
|
||||
}
|
||||
|
||||
/// For subclasses that want to use a custom accessory view.
|
||||
open var customAccessoryView = false
|
||||
|
||||
open var topMarginPadding: CGFloat = 24
|
||||
open var bottomMarginPadding: CGFloat = 24
|
||||
|
||||
private var heroAccessoryCenter: CGPoint?
|
||||
|
||||
// MARK: - Styling
|
||||
open func style(with styleString: String?) {
|
||||
guard let styleString = styleString else {
|
||||
return
|
||||
}
|
||||
switch styleString {
|
||||
case "standard":
|
||||
styleStandard()
|
||||
case "header":
|
||||
styleHeader()
|
||||
case "none":
|
||||
styleNone()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
open func styleStandard() {
|
||||
topMarginPadding = 24
|
||||
bottomMarginPadding = 24
|
||||
bottomSeparatorView?.show()
|
||||
bottomSeparatorView?.setAsLight()
|
||||
}
|
||||
|
||||
open func styleHeader() {
|
||||
topMarginPadding = 48
|
||||
bottomMarginPadding = 16
|
||||
bottomSeparatorView?.show()
|
||||
bottomSeparatorView?.setAsRegular()
|
||||
}
|
||||
|
||||
open func styleNone() {
|
||||
topMarginPadding = 0
|
||||
bottomMarginPadding = 0
|
||||
bottomSeparatorView?.hide()
|
||||
}
|
||||
|
||||
/// Adds the molecule to the view.
|
||||
open func addMolecule(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) {
|
||||
contentView.addSubview(molecule)
|
||||
let standardConstraints = (molecule as? MVMCoreUIViewConstrainingProtocol)?.useStandardConstraints?() ?? true
|
||||
NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: molecule, useMargins: standardConstraints).values))
|
||||
|
||||
// This molecule will by default handle margins.
|
||||
if let castView = molecule as? MVMCoreUIViewConstrainingProtocol {
|
||||
castView.shouldSetHorizontalMargins?(false)
|
||||
castView.shouldSetVerticalMargins?(false)
|
||||
}
|
||||
self.molecule = molecule
|
||||
}
|
||||
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
// Ensures accessory view aligns to the center y derived from the
|
||||
if let center = heroAccessoryCenter {
|
||||
accessoryView?.center.y = center.y
|
||||
}
|
||||
}
|
||||
|
||||
// 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.setMarginsFor(self, size: size, defaultHorizontal: updateViewHorizontalDefaults, top: topMarginPadding, bottom: bottomMarginPadding)
|
||||
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)
|
||||
|
||||
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() {
|
||||
selectionStyle = .none
|
||||
insetsLayoutMarginsFromSafeArea = false
|
||||
contentView.insetsLayoutMarginsFromSafeArea = false
|
||||
contentView.preservesSuperviewLayoutMargins = false
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
self.json = json
|
||||
|
||||
style(with: json?.optionalStringForKey("style"))
|
||||
|
||||
if let useHorizontalMargins = json?.optionalBoolForKey("useHorizontalMargins") {
|
||||
updateViewHorizontalDefaults = useHorizontalMargins
|
||||
}
|
||||
|
||||
if (json?.optionalBoolForKey("useVerticalMargins") ?? true) == false {
|
||||
topMarginPadding = 0
|
||||
bottomMarginPadding = 0
|
||||
}
|
||||
|
||||
if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) {
|
||||
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
|
||||
}
|
||||
|
||||
// Add the caret if there is an action and it's not declared hidden.
|
||||
if !customAccessoryView {
|
||||
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)
|
||||
}
|
||||
|
||||
guard let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else { return }
|
||||
molecule?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData)
|
||||
|
||||
// This molecule will by default handle margins.
|
||||
if let castView = molecule as? MVMCoreUIViewConstrainingProtocol {
|
||||
castView.shouldSetHorizontalMargins?(false)
|
||||
castView.shouldSetVerticalMargins?(false)
|
||||
}
|
||||
}
|
||||
|
||||
public func reset() {
|
||||
molecule?.reset?()
|
||||
updateViewHorizontalDefaults = true
|
||||
styleStandard()
|
||||
backgroundColor = .white
|
||||
}
|
||||
|
||||
public class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
|
||||
guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) else {
|
||||
return 80
|
||||
}
|
||||
return max(2 * PaddingDefaultVerticalSpacing3, height)
|
||||
}
|
||||
|
||||
public class func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
return molecule?.optionalStringForKey(KeyMoleculeName) ?? ""
|
||||
}
|
||||
|
||||
// MARK: - Arrow
|
||||
/// Adds the standard mvm style caret to the accessory view
|
||||
@objc 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
|
||||
}
|
||||
|
||||
/// NOTE: Should only be called when displayed or about to be displayed.
|
||||
public func alignAccessoryToHero() {
|
||||
|
||||
// Layout call required to force draw in memory to get dimensions of subviews.
|
||||
layoutIfNeeded()
|
||||
guard let heroLabel = findHeroLabel(views: contentView.subviews), let hero = heroLabel.hero else { return }
|
||||
let rect = Label.boundingRect(forCharacterRange: NSRange(location: hero, length: 1), in: heroLabel)
|
||||
accessoryView?.center.y = contentView.convert(UIView(frame: rect).center, from: heroLabel).y
|
||||
heroAccessoryCenter = accessoryView?.center
|
||||
}
|
||||
|
||||
/// Traverses the view hierarchy for a 🦸♂️heroic Label.
|
||||
private func findHeroLabel(views: [UIView]) -> Label? {
|
||||
|
||||
if views.isEmpty {
|
||||
return nil
|
||||
}
|
||||
|
||||
var queue = [UIView]()
|
||||
|
||||
for view in views {
|
||||
// Only one Label will have a hero in a table cell.
|
||||
if let label = view as? Label, label.hero != nil {
|
||||
return label
|
||||
}
|
||||
queue.append(contentsOf: view.subviews)
|
||||
}
|
||||
|
||||
return findHeroLabel(views: queue)
|
||||
}
|
||||
|
||||
// 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(TableViewCell.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)
|
||||
}
|
||||
}
|
||||
|
||||
public func willDisplay() {
|
||||
alignAccessoryToHero()
|
||||
}
|
||||
|
||||
// MARK: - Separator
|
||||
open 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class TabsTableViewCell: MoleculeTableViewCell {
|
||||
@objcMembers public class TabsTableViewCell: TableViewCell {
|
||||
let tabs = TopTabbar(frame: .zero)
|
||||
var delegateObject: MVMCoreUIDelegateObject?
|
||||
var previousTabIndex = 0
|
||||
|
||||
@ -62,7 +62,7 @@ open class ModuleMolecule: ViewConstrainingView {
|
||||
return MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: module)?.estimatedHeight?(forRow: module, delegateObject: delegateObject) ?? 0
|
||||
}
|
||||
|
||||
public override static func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
public override class func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
guard let moduleName = molecule?.optionalStringForKey("moduleName"), let module = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else {
|
||||
// Critical error
|
||||
return "moduleMolecule<>"
|
||||
@ -70,7 +70,7 @@ open class ModuleMolecule: ViewConstrainingView {
|
||||
return "moduleMolecule<" + (MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: module)?.name?(forReuse: module, delegateObject: delegateObject) ?? module.stringForkey(KeyMoleculeName)) + ">"
|
||||
}
|
||||
|
||||
public override static func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||
public override class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||
let moduleName = json?.optionalStringForKey("moduleName")
|
||||
if moduleName == nil || delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) == nil {
|
||||
if let errorObject = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), code: CoreUIErrorCode.ErrorCodeModuleMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) {
|
||||
|
||||
@ -184,7 +184,7 @@ public class MoleculeStackView: ViewConstrainingView {
|
||||
}
|
||||
}
|
||||
|
||||
public override static func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
public override class func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
// This will aggregate names of molecules to make an id.
|
||||
guard let molecules = molecule?.optionalArrayForKey(KeyMolecules) else {
|
||||
return "stack<>"
|
||||
@ -221,7 +221,7 @@ public class MoleculeStackView: ViewConstrainingView {
|
||||
return estimatedHeight
|
||||
}
|
||||
|
||||
public override static func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||
public override class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||
guard let items = json?.optionalArrayForKey(KeyMolecules) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user