diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 6fd71e7d..96c5476a 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -67,6 +67,7 @@ D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */; }; D260D7B622D68514007E7233 /* MVMCoreUIPagingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; D274CA332236A78900B01B62 /* StandardFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D274CA322236A78900B01B62 /* StandardFooterView.swift */; }; + D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2755D7A23689C7500485468 /* TableViewCell.swift */; }; D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */; }; D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27CD40F2339057800C1DC07 /* EyebrowHeadlineBodyLink.swift */; }; D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */; }; @@ -256,6 +257,7 @@ D260D7B022D65BDD007E7233 /* MVMCoreUIPageControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIPageControl.m; sourceTree = ""; }; D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPagingProtocol.h; sourceTree = ""; }; D274CA322236A78900B01B62 /* StandardFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardFooterView.swift; sourceTree = ""; }; + D2755D7A23689C7500485468 /* TableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewCell.swift; sourceTree = ""; }; D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsTableViewCell.swift; sourceTree = ""; }; D27CD40F2339057800C1DC07 /* EyebrowHeadlineBodyLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EyebrowHeadlineBodyLink.swift; sourceTree = ""; }; D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFLoadImageView.swift; sourceTree = ""; }; @@ -485,6 +487,7 @@ D22479912316A9EF003FCCF9 /* Items */ = { isa = PBXGroup; children = ( + D2755D7A23689C7500485468 /* TableViewCell.swift */, 01509D8E2327EC6F00EF99AA /* MoleculeTableViewCell.swift */, D2A6390422CBCE160052ED1F /* MoleculeCollectionViewCell.swift */, D224799A231965AD003FCCF9 /* AccordionMoleculeTableViewCell.swift */, @@ -582,7 +585,6 @@ D29DF10E21E67A77003B2FB9 /* Molecules */ = { isa = PBXGroup; children = ( - 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */, D22479912316A9EF003FCCF9 /* Items */, D224798F2316A99F003FCCF9 /* LeftRightViews */, D224798E2316A995003FCCF9 /* HorizontalCombinationViews */, @@ -598,6 +600,7 @@ D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */, D2D6CD3F22E78C1A00D701B8 /* Scroller.swift */, 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */, + 017BEB372360C6AC0024EF95 /* RadioButtonLabel.swift */, ); path = Molecules; sourceTree = ""; @@ -1087,6 +1090,7 @@ D29DF27621E79E81003B2FB9 /* MVMCoreUILoggingHandler.m in Sources */, D29DF2A221E7AF4E003B2FB9 /* MVMCoreUIUtility.m in Sources */, D29DF12B21E6851E003B2FB9 /* MVMCoreUITopAlertExpandableView.m in Sources */, + D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */, 0A21DB85235E06EF00C160A2 /* MFTextField.m in Sources */, B8200E152280C4CF007245F4 /* ProgressBar.swift in Sources */, D29DF25421E6A177003B2FB9 /* MFMdnTextField.m in Sources */, diff --git a/MVMCoreUI/Atoms/Buttons/ButtonView.swift b/MVMCoreUI/Atoms/Buttons/ButtonView.swift index 0fc0f3c3..088c4a00 100644 --- a/MVMCoreUI/Atoms/Buttons/ButtonView.swift +++ b/MVMCoreUI/Atoms/Buttons/ButtonView.swift @@ -64,7 +64,7 @@ import UIKit primaryButton?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 42 } diff --git a/MVMCoreUI/Atoms/Buttons/CaretButton.swift b/MVMCoreUI/Atoms/Buttons/CaretButton.swift index 759dfdb4..8f40568c 100644 --- a/MVMCoreUI/Atoms/Buttons/CaretButton.swift +++ b/MVMCoreUI/Atoms/Buttons/CaretButton.swift @@ -138,7 +138,7 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI return UIStackView.Alignment.leading; } - public static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 10 } } diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index 83a44659..109f2f97 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -266,7 +266,7 @@ public typealias ActionBlock = () -> () let length = attribute["length"] as? Int else { continue } - var range = NSRange(location: location, length: length) + let range = NSRange(location: location, length: length) switch attributeType { case "underline": @@ -278,17 +278,7 @@ public typealias ActionBlock = () -> () case "color": if let colorHex = attribute.optionalStringForKey(KeyTextColor), !colorHex.isEmpty { - // crash fix: removing attribute, even though it does not exists - let foregroundColorAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in - if attribute.key == .foregroundColor { - return true - } else { - return false - } - } - if foregroundColorAttributesArray.isEmpty == false { - attributedString.removeAttribute(.foregroundColor, range: range) - } + attributedString.removeAttribute(.foregroundColor, range: range) attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range) } case "image": @@ -310,28 +300,8 @@ public typealias ActionBlock = () -> () case "font": if let fontStyle = attribute.optionalStringForKey("style") { let styles = MFStyler.styleGetAttributedString("0", withStyle: fontStyle) - // crash fix: removing font attribute, even though it does not exists - let fontAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in - if attribute.key == .font { - return true - } else { - return false - } - } - if fontAttributesArray.isEmpty == false { - attributedString.removeAttribute(.font, range: range) - } - // crash fix: removing attribute, even though it does not exists - let foregroundColorAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in - if attribute.key == .foregroundColor { - return true - } else { - return false - } - } - if foregroundColorAttributesArray.isEmpty == false { - attributedString.removeAttribute(.foregroundColor, range: range) - } + attributedString.removeAttribute(.font, range: range) + attributedString.removeAttribute(.foregroundColor, range: range) attributedString.addAttributes(styles.attributes(at: 0, effectiveRange: nil), range: range) } else { let fontSize = attribute["size"] as? CGFloat @@ -344,17 +314,7 @@ public typealias ActionBlock = () -> () } if let font = font { - // crash fix: removing font attribute, even though it does not exists - let fontAttributesArray = attributedString.attributes(at: location, effectiveRange: &range).filter { (attribute) -> Bool in - if attribute.key == .font { - return true - } else { - return false - } - } - if fontAttributesArray.isEmpty == false { - attributedString.removeAttribute(.font, range: range) - } + attributedString.removeAttribute(.font, range: range) attributedString.addAttribute(.font, value: font, range: range) } } diff --git a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift index 21802b38..361c0aa2 100644 --- a/MVMCoreUI/Atoms/Views/MFLoadImageView.swift +++ b/MVMCoreUI/Atoms/Views/MFLoadImageView.swift @@ -215,7 +215,7 @@ import UIKit pinEdges(.all) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return json?.optionalCGFloatForKey("height") ?? 0 } diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m index 2e32d73b..ae0db4a7 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUISwitch.m @@ -145,7 +145,8 @@ const CGFloat SwitchShakeIntensity = 2; - (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData { self.json = json; - + self.delegate = delegateObject; + [FormValidator setupValidationWithMolecule:self delegate:delegateObject.formValidationProtocol]; NSString *color = [json string:@"onTintColor"]; @@ -169,8 +170,7 @@ const CGFloat SwitchShakeIntensity = 2; } [self setState:[json boolForKey:@"state"] animated:false]; - - self.delegate = delegateObject; + NSDictionary *actionMap = [json dict:@"actionMap"]; if (actionMap) { [self addTarget:self action:@selector(addCustomAction) forControlEvents:UIControlEventTouchUpInside]; diff --git a/MVMCoreUI/Atoms/Views/ProgressBar.swift b/MVMCoreUI/Atoms/Views/ProgressBar.swift index a9333966..1aecd537 100644 --- a/MVMCoreUI/Atoms/Views/ProgressBar.swift +++ b/MVMCoreUI/Atoms/Views/ProgressBar.swift @@ -73,7 +73,7 @@ import Foundation trackTintColor = UIColor.mfLightSilver() } - public static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 8 } } diff --git a/MVMCoreUI/BaseClasses/View.swift b/MVMCoreUI/BaseClasses/View.swift index 735243df..bc6e7634 100644 --- a/MVMCoreUI/BaseClasses/View.swift +++ b/MVMCoreUI/BaseClasses/View.swift @@ -8,47 +8,46 @@ import UIKit -public class View: UIView { - var json: [AnyHashable: Any]? +@objcMembers open class View: UIView { + open var json: [AnyHashable: Any]? private var initialSetupPerformed = false public override init(frame: CGRect) { - super.init(frame: .zero) - initialSetup() - } + super.init(frame: .zero) + initialSetup() + } - init() { - super.init(frame: .zero) - initialSetup() - } - - public required init?(coder: NSCoder) { - super.init(coder: coder) - initialSetup() - } - - public func initialSetup() { - if !initialSetupPerformed { - initialSetupPerformed = true - setupView() - } + public init() { + super.init(frame: .zero) + initialSetup() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + initialSetup() + } + + public func initialSetup() { + if !initialSetupPerformed { + initialSetupPerformed = true + setupView() } } +} extension View: MVMCoreViewProtocol { - public func updateView(_ size: CGFloat) { - } + open func updateView(_ size: CGFloat) {} /// Will be called only once. - public func setupView() { + open func setupView() { translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false } } extension View: MVMCoreUIMoleculeViewProtocol { - public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + open func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { self.json = json if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) { @@ -56,7 +55,7 @@ extension View: MVMCoreUIMoleculeViewProtocol { } } - public func reset() { + open func reset() { backgroundColor = .clear } } diff --git a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift index d698b728..5045d5a4 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerTableViewController.swift @@ -61,11 +61,6 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { return nil } - /// Space between the bottom view and the table sections, nil to fill. nil default - open func spaceBelowBottomView() -> CGFloat? { - return nil - } - /// can override to return a minimum fill space. open func minimumFillSpace() -> CGFloat { return 0 @@ -159,7 +154,7 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { bottomViewTopConstraint?.isActive = true bottomView.leftAnchor.constraint(equalTo: footerView.leftAnchor).isActive = true footerView.rightAnchor.constraint(equalTo: bottomView.rightAnchor).isActive = true - footerView.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor, constant: spaceBelowBottomView() ?? 0).isActive = true + footerView.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor).isActive = true self.footerView = footerView showFooter(nil) } @@ -219,14 +214,16 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController { /// Subclass for a top view. open func viewForTop() -> UIView { let view = MVMCoreUICommonViewsUtility.commonView() - view.heightAnchor.constraint(equalToConstant: 0).isActive = true + // Small height is needed to stop apple from adding padding for grouped tables when no header. + view.heightAnchor.constraint(equalToConstant: 1).isActive = true return view } /// Subclass for a bottom view. open func viewForBottom() -> UIView { + // Default spacing is standard when no buttons. let view = MVMCoreUICommonViewsUtility.commonView() - view.heightAnchor.constraint(equalToConstant: 0).isActive = true + view.heightAnchor.constraint(equalToConstant: PaddingDefaultVerticalSpacing).isActive = true return view } diff --git a/MVMCoreUI/Molecules/ActionDetailWithImage.swift b/MVMCoreUI/Molecules/ActionDetailWithImage.swift index 86cee41b..4811f20d 100644 --- a/MVMCoreUI/Molecules/ActionDetailWithImage.swift +++ b/MVMCoreUI/Molecules/ActionDetailWithImage.swift @@ -91,7 +91,7 @@ import UIKit imageLoader.updateView(size) } - public override static func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 197 } diff --git a/MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift b/MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift index aca450aa..6e10a1a0 100644 --- a/MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift +++ b/MVMCoreUI/Molecules/HorizontalCombinationViews/ImageHeadlineBody.swift @@ -82,7 +82,7 @@ import UIKit imageView.reset() } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 95 } } diff --git a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift index 685d2e7a..75249f7d 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeCollectionViewCell.swift @@ -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 } diff --git a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift index 2ae52775..4769b7e5 100644 --- a/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/MoleculeTableViewCell.swift @@ -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 static 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?) -> [String]? { + 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 } 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() - } - } } diff --git a/MVMCoreUI/Molecules/Items/TableViewCell.swift b/MVMCoreUI/Molecules/Items/TableViewCell.swift new file mode 100644 index 00000000..cc531d75 --- /dev/null +++ b/MVMCoreUI/Molecules/Items/TableViewCell.swift @@ -0,0 +1,314 @@ +// +// 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, 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. + 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 + case allExceptTop + case allExceptBottom + case 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 + + guard let json = json else { return } + + 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 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() + } + } +} diff --git a/MVMCoreUI/Molecules/Items/TabsTableViewCell.swift b/MVMCoreUI/Molecules/Items/TabsTableViewCell.swift index 1c509a8f..1a1796cd 100644 --- a/MVMCoreUI/Molecules/Items/TabsTableViewCell.swift +++ b/MVMCoreUI/Molecules/Items/TabsTableViewCell.swift @@ -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 diff --git a/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift b/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift index 06917608..9b7dc062 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/CornerLabels.swift @@ -174,7 +174,7 @@ import UIKit bottomRightLabel.styleB3(true) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 34 } } diff --git a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift index f585c2da..79110460 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodySwitch.swift @@ -19,7 +19,7 @@ import UIKit mvmSwitch.updateView(size) } - public override func setupView() { + open override func setupView() { super.setupView() guard mvmSwitch.superview == nil else { return @@ -41,18 +41,18 @@ import UIKit mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 30 } - public override func setAsMolecule() { + open override func setAsMolecule() { super.setAsMolecule() headlineBody.setAsMolecule() (mvmSwitch as MVMCoreUIMoleculeViewProtocol).setAsMolecule?() headlineBody.styleListItem() } - public override func reset() { + open override func reset() { super.reset() headlineBody.reset() (mvmSwitch as MVMCoreUIMoleculeViewProtocol).reset?() diff --git a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodyTextButtonSwitch.swift b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodyTextButtonSwitch.swift index 0f184544..b0cf6956 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodyTextButtonSwitch.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/HeadlineBodyTextButtonSwitch.swift @@ -41,7 +41,7 @@ import UIKit mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return HeadlineBodyTextButton.estimatedHeight(forRow: json, delegateObject: delegateObject) } diff --git a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelSwitch.swift b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelSwitch.swift index d47cf44c..f60202b3 100644 --- a/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelSwitch.swift +++ b/MVMCoreUI/Molecules/LeftRightViews/SwitchMolecules/LabelSwitch.swift @@ -41,7 +41,7 @@ import UIKit mvmSwitch.setWithJSON(json?.optionalDictionaryForKey("switch"), delegateObject: delegateObject, additionalData: additionalData) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return MVMCoreUISwitch.estimatedHeight(forRow: json, delegateObject: delegateObject) } diff --git a/MVMCoreUI/Molecules/ModuleMolecule.swift b/MVMCoreUI/Molecules/ModuleMolecule.swift index c993b0c2..77acc007 100644 --- a/MVMCoreUI/Molecules/ModuleMolecule.swift +++ b/MVMCoreUI/Molecules/ModuleMolecule.swift @@ -54,7 +54,7 @@ open class ModuleMolecule: ViewConstrainingView { moduleMolecule?.reset?() } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { guard let moduleName = json?.optionalStringForKey("moduleName"), let module = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else { // Critical error return 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?) -> [String]? { + public override class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [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)) { diff --git a/MVMCoreUI/Molecules/StandardFooterView.swift b/MVMCoreUI/Molecules/StandardFooterView.swift index 7734883e..7c9c9282 100644 --- a/MVMCoreUI/Molecules/StandardFooterView.swift +++ b/MVMCoreUI/Molecules/StandardFooterView.swift @@ -26,7 +26,7 @@ open class StandardFooterView: ViewConstrainingView { (molecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(false) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { if let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) { return height + PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing } diff --git a/MVMCoreUI/Molecules/StandardHeaderView.swift b/MVMCoreUI/Molecules/StandardHeaderView.swift index 12819313..d54bde87 100644 --- a/MVMCoreUI/Molecules/StandardHeaderView.swift +++ b/MVMCoreUI/Molecules/StandardHeaderView.swift @@ -62,7 +62,7 @@ public class StandardHeaderView: ViewConstrainingView { separatorView?.show() } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { if let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let height = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON)?.estimatedHeight?(forRow: moleculeJSON, delegateObject: delegateObject) { return height + PaddingDefaultVerticalSpacing + PaddingDefaultVerticalSpacing } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift index 306cf690..a7d96a34 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/EyebrowHeadlineBodyLink.swift @@ -64,7 +64,7 @@ import UIKit body.styleB2(true) } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 65 } } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift index e63eb2ba..d236fc37 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBody.swift @@ -136,7 +136,7 @@ open class HeadlineBody: ViewConstrainingView { stylePageHeader() } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 58 } } diff --git a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyTextButton.swift b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyTextButton.swift index 009189cb..7c24cff4 100644 --- a/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyTextButton.swift +++ b/MVMCoreUI/Molecules/VerticalCombinationViews/HeadlineBodyTextButton.swift @@ -76,7 +76,7 @@ import UIKit textButton.reset() } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 60 } } diff --git a/MVMCoreUI/Organisms/MoleculeStackView.swift b/MVMCoreUI/Organisms/MoleculeStackView.swift index 95855f52..cafe21b0 100644 --- a/MVMCoreUI/Organisms/MoleculeStackView.swift +++ b/MVMCoreUI/Organisms/MoleculeStackView.swift @@ -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<>" @@ -199,7 +199,7 @@ public class MoleculeStackView: ViewConstrainingView { return name } - public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + public override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { guard let items = json?.optionalArrayForKey(KeyMolecules) else { return 0 } @@ -221,7 +221,7 @@ public class MoleculeStackView: ViewConstrainingView { return estimatedHeight } - public override static func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { + public override class func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { guard let items = json?.optionalArrayForKey(KeyMolecules) else { return nil } diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 9df9fbc2..ea2913c2 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -42,11 +42,6 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { return molecule } - // for bottom gutter/free space - open override func spaceBelowBottomView() -> CGFloat? { - return PaddingDefaultVerticalSpacing - } - open override func newDataBuildScreen() { super.newDataBuildScreen() setup() @@ -131,7 +126,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { open override func addMolecules(_ molecules: [[AnyHashable : Any]], 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 cell = sender as? MoleculeTableViewCell, let indexPath = self.tableView?.indexPath(for: cell) else { return } + guard let indexPath = self.tableView?.indexPath(for: sender) else { return } var indexPaths: [IndexPath] = [] for molecule in molecules { if let info = self.getMoleculeInfo(with: molecule) { @@ -158,23 +153,6 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { } } self.tableView?.deleteRows(at: indexPaths, with: animation) - // crash fix - self.tableView?.reloadData() - self.updateViewConstraints() - self.view.layoutIfNeeded() - } - - public func removeListItem(_ molecule: [AnyHashable : Any], animation: UITableView.RowAnimation) { - var indexPaths: [IndexPath] = [] - if let removeIndex = moleculesInfo?.firstIndex(where: { (moleculeInfo) -> Bool in - return NSDictionary(dictionary: molecule).isEqual(to: moleculeInfo.molecule["molecule"] as? [AnyHashable : Any] ?? [:]) - }) { - moleculesInfo?.remove(at: removeIndex) - indexPaths.append(IndexPath(row: removeIndex + indexPaths.count, section: 0)) - } - self.tableView?.deleteRows(at: indexPaths, with: animation) - // crash fix - self.tableView?.reloadData() self.updateViewConstraints() self.view.layoutIfNeeded() } diff --git a/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m index fd5c9b21..4a21b1a5 100644 --- a/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m +++ b/MVMCoreUI/Utility/MVMCoreUICommonViewsUtility.m @@ -33,6 +33,7 @@ static const CGFloat VertialShadowOffset = 6; UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; view.translatesAutoresizingMaskIntoConstraints = NO; view.backgroundColor = [UIColor clearColor]; + view.directionalLayoutMargins = NSDirectionalEdgeInsetsZero; return view; }