Merge branch 'bugfix/molecules' into 'develop'

re-molecule image view

See merge request BPHV_MIPS/mvm_core_ui!88
This commit is contained in:
Pfeil, Scott Robert 2019-06-28 15:29:36 -04:00
commit e3c6c004da
27 changed files with 533 additions and 381 deletions

View File

@ -39,7 +39,6 @@
D28B4F8B21FF967C00712C7A /* MVMCoreUIObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D28B4F8921FF967C00712C7A /* MVMCoreUIObject.m */; };
D296E13C229598BF0051EBE7 /* MoleculeListCellProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D296E13B2295969C0051EBE7 /* MoleculeListCellProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
D296E1412295EBBA0051EBE7 /* MoleculeDelegateProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D296E1402295EBBA0051EBE7 /* MoleculeDelegateProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
D296E143229729C30051EBE7 /* MoleculeMappingObject+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D296E142229729C30051EBE7 /* MoleculeMappingObject+Extension.swift */; };
D296E14722A5984C0051EBE7 /* MVMCoreUIViewConstrainingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29770C821F7C4AE00B2F0D0 /* TopLabelsView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */; };
D29770C921F7C4AE00B2F0D0 /* TopLabelsView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29770C721F7C4AE00B2F0D0 /* TopLabelsView.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -49,6 +48,7 @@
D29770F521F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D29770F121F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsViewController.m */; };
D29770FC21F7C77400B2F0D0 /* MVMCoreUITextFieldView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29770FA21F7C77400B2F0D0 /* MVMCoreUITextFieldView.m */; };
D29770FD21F7C77400B2F0D0 /* MVMCoreUITextFieldView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29770FB21F7C77400B2F0D0 /* MVMCoreUITextFieldView.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */; };
D29DF0D121E404D4003B2FB9 /* MVMCoreUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */; };
D29DF11521E6805F003B2FB9 /* UIColor+MFConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF11121E6805F003B2FB9 /* UIColor+MFConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -207,7 +207,6 @@
D28B4F8921FF967C00712C7A /* MVMCoreUIObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUIObject.m; sourceTree = "<group>"; };
D296E13B2295969C0051EBE7 /* MoleculeListCellProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MoleculeListCellProtocol.h; sourceTree = "<group>"; };
D296E1402295EBBA0051EBE7 /* MoleculeDelegateProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MoleculeDelegateProtocol.h; sourceTree = "<group>"; };
D296E142229729C30051EBE7 /* MoleculeMappingObject+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MoleculeMappingObject+Extension.swift"; sourceTree = "<group>"; };
D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewConstrainingProtocol.h; sourceTree = "<group>"; };
D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TopLabelsView.m; sourceTree = "<group>"; };
D29770C721F7C4AE00B2F0D0 /* TopLabelsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopLabelsView.h; sourceTree = "<group>"; };
@ -217,6 +216,7 @@
D29770F121F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TopLabelsAndBottomButtonsViewController.m; sourceTree = "<group>"; };
D29770FA21F7C77400B2F0D0 /* MVMCoreUITextFieldView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVMCoreUITextFieldView.m; sourceTree = "<group>"; };
D29770FB21F7C77400B2F0D0 /* MVMCoreUITextFieldView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUITextFieldView.h; sourceTree = "<group>"; };
D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleMolecule.swift; sourceTree = "<group>"; };
D29DF0CC21E404D4003B2FB9 /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D29DF0CF21E404D4003B2FB9 /* MVMCoreUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUI.h; sourceTree = "<group>"; };
D29DF0D021E404D4003B2FB9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -471,6 +471,7 @@
D2E1FADC2268B25E00AEFD8C /* MoleculeTableViewCell.swift */,
B8200E142280C4CF007245F4 /* ProgressBar.swift */,
B8200E182281DC1A007245F4 /* ProgressBarWithLabel.swift */,
D29B770F22C281F400D6ACE0 /* ModuleMolecule.swift */,
);
path = Molecules;
sourceTree = "<group>";
@ -679,7 +680,6 @@
D296E1402295EBBA0051EBE7 /* MoleculeDelegateProtocol.h */,
D2A514562211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.h */,
D2A514572211C53C00345BFB /* MVMCoreUIMoleculeMappingObject.m */,
D296E142229729C30051EBE7 /* MoleculeMappingObject+Extension.swift */,
);
path = OtherHandlers;
sourceTree = "<group>";
@ -901,6 +901,7 @@
D29DF32121ED0CBA003B2FB9 /* LabelView.m in Sources */,
DBC4391822442197001AB423 /* CaretView.swift in Sources */,
D29770F221F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsTableViewController.m in Sources */,
D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */,
DBC4391922442197001AB423 /* DashLine.swift in Sources */,
D29DF29621E7ADB8003B2FB9 /* StackableViewController.m in Sources */,
D2E1FADB2260D3D200AEFD8C /* MVMCoreUIDelegateObject.swift in Sources */,
@ -944,7 +945,6 @@
D29DF28321E7AB24003B2FB9 /* MVMCoreUICommonViewsUtility.m in Sources */,
D206997821FB8A0B00CAE0DE /* MVMCoreUINavigationController.m in Sources */,
D29DF27A21E7A533003B2FB9 /* MVMCoreUISession.m in Sources */,
D296E143229729C30051EBE7 /* MoleculeMappingObject+Extension.swift in Sources */,
01DF55E021F8FAA800CC099B /* MFTextFieldListView.swift in Sources */,
D2A5146B2214905000345BFB /* ThreeLayerViewController.swift in Sources */,
D29DF2C921E7BFC6003B2FB9 /* MFSizeObject.m in Sources */,

View File

@ -669,6 +669,13 @@
[FormValidator setupValidationWithMolecule:self delegate:delegateObject.formValidationProtocol];
self.primaryButtonType = PrimaryButtonTypeCustom;
NSString *style = [json string:@"style"];
if ([style isEqualToString:@"primary"]) {
[self setAsStandardCustom];
} else if ([style isEqualToString:@"secondary"]) {
[self setAsSecondaryCustom];
}
NSString *color = [json string:@"fillColor"];
if (color) {
self.fillColor = [UIColor mfGetColorForHex:color];
@ -691,7 +698,14 @@
}
self.validationRequired = [json boolForKey:@"validationRequired"];
[self setAsSmallButton:[json boolForKey:@"small"]];
NSString *size = [json string:@"size"];
if ([size isEqualToString:@"small"]) {
[self setAsSmallButton:YES];
} else if ([size isEqualToString:@"tiny"]) {
[self setAsTiny:YES];
} else {
[self setAsSmallButton:NO];
}
[self setWithActionMap:json delegateObject:delegateObject additionalData:additionalData];
}

View File

@ -108,10 +108,6 @@ open class CaretView: MFView {
// Configure class properties with JSON values
guard let dictionary = json else { return }
if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String {
backgroundColor = UIColor.mfGet(forHex: backgroundColorHex)
}
if let strokeColorHex = dictionary["strokeColor"] as? String {
strokeColor = UIColor.mfGet(forHex: strokeColorHex)
}

View File

@ -74,10 +74,6 @@ open class DashLine: MFView {
// Configure class properties with JSON values
guard let jsonDictionary = json else { return }
if let backgroundColorHex = jsonDictionary[KeyBackgroundColor] as? String {
backgroundColor = UIColor.mfGet(forHex: backgroundColorHex)
}
if let isHiddenValue = jsonDictionary[KeyIsHidden] as? Bool {
isHidden = isHiddenValue
}

View File

@ -247,10 +247,11 @@ public typealias ActionBlock = () -> Void
case "action":
guard let actionLabel = label as? Label else { continue }
actionLabel.addTappableLinkAttribute(range: range,
actionMap: json,
additionalData: additionalData,
delegateObject: delegate)
actionLabel.addActionAttributes(range: range, string: attributedString)
actionLabel.clauses.append(ActionableClause(range: range,
actionBlock: actionLabel.createActionBlockFrom(actionMap: json,
additionalData: additionalData,
delegateObject: delegate)))
default:
continue
}
@ -397,13 +398,18 @@ extension Label {
}
}
fileprivate func setDefaultAttributes(range: NSRange) {
func addActionAttributes(range: NSRange, string: NSMutableAttributedString?) {
guard let string = string else { return }
string.addAttributes([NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue], range: range)
}
fileprivate func setActionAttributes(range: NSRange) {
guard let attributedText = attributedText else { return }
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedText)
mutableAttributedString.addAttributes([NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue], range: range)
addActionAttributes(range: range, string: mutableAttributedString)
self.attributedText = mutableAttributedString
}
@ -417,7 +423,7 @@ extension Label {
*/
@objc public func addTappableLinkAttribute(range: NSRange, actionBlock: @escaping ActionBlock) {
setDefaultAttributes(range: range)
setActionAttributes(range: range)
clauses.append(ActionableClause(range: range, actionBlock: actionBlock))
}
@ -433,7 +439,7 @@ extension Label {
*/
@objc public func addTappableLinkAttribute(range: NSRange, actionMap: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?, delegateObject: DelegateObject?) {
setDefaultAttributes(range: range)
setActionAttributes(range: range)
clauses.append(ActionableClause(range: range,
actionBlock: createActionBlockFrom(actionMap: actionMap,
additionalData: additionalData,

View File

@ -170,10 +170,6 @@ import Foundation
leftTextLabel.setWithJSON(dictionary.optionalDictionaryForKey("leftText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData)
rightTextLabel.setWithJSON(dictionary.optionalDictionaryForKey("rightText"), delegateObject: delegateObject as? MVMCoreUIDelegateObject, additionalData: additionalData)
if let backgroundColorHex = dictionary[KeyBackgroundColor] as? String {
backgroundColor = UIColor.mfGet(forHex: backgroundColorHex)
}
if !leftTextLabel.hasText {
constrainRightLabel()
} else if !rightTextLabel.hasText {

View File

@ -18,12 +18,17 @@ import UIKit
var heightConstraint: NSLayoutConstraint?
var loadingSpinnerHeightConstraint: NSLayoutConstraint?
// Allows for a view to hardcode which height to use if there is none in the json.
var imageWidth: CGFloat?
var imageHeight: CGFloat?
// For keeping track of current state.
var edges: UIRectEdge?
var spinnerHeight: CGFloat?
var width: CGFloat?
var loadingImageName: String?
var isFallbackImage: Bool = false
private var edges: UIRectEdge?
private var spinnerHeight: CGFloat?
private var currentImageWidth: CGFloat?
private var currentImageHeight: CGFloat?
private var currentImageName: String?
private var isFallbackImage: Bool = false
public init(pinnedEdges edge: UIRectEdge) {
edges = edge
@ -50,62 +55,24 @@ import UIKit
imageView.setContentHuggingPriority(UILayoutPriority(rawValue: 900), for: NSLayoutConstraint.Axis.vertical)
}
if let topPin = topPin {
removeConstraint(topPin)
}
if edge.contains(UIRectEdge.top) {
topPin = imageView.topAnchor.constraint(equalTo: topAnchor)
if edge.contains(UIRectEdge.top) && edge.contains(UIRectEdge.bottom) {
alignFillVertical()
} else if edge.contains(UIRectEdge.top) {
alignTop()
} else if edge.contains(UIRectEdge.bottom) {
alignBottom()
} else {
topPin = imageView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor)
alignCenterVertical()
}
topPin?.isActive = true
if let bottomPin = bottomPin {
removeConstraint(bottomPin)
}
if edge.contains(UIRectEdge.bottom) {
bottomPin = bottomAnchor.constraint(equalTo: imageView.bottomAnchor)
if edge.contains(UIRectEdge.left) && edge.contains(UIRectEdge.right) {
alignFillHorizontal()
} else if edge.contains(UIRectEdge.left) {
alignLeft()
} else if edge.contains(UIRectEdge.right) {
alignRight()
} else {
bottomPin = bottomAnchor.constraint(greaterThanOrEqualTo: imageView.bottomAnchor)
}
bottomPin?.isActive = true
if let leftPin = leftPin {
removeConstraint(leftPin)
}
if edge.contains(UIRectEdge.left) {
leftPin = imageView.leftAnchor.constraint(equalTo: leftAnchor)
} else {
leftPin = imageView.leftAnchor.constraint(greaterThanOrEqualTo: leftAnchor)
}
leftPin?.isActive = true
if let rightPin = rightPin {
removeConstraint(rightPin)
}
if edge.contains(UIRectEdge.right) {
rightPin = rightAnchor.constraint(equalTo: imageView.rightAnchor)
} else {
rightPin = rightAnchor.constraint(greaterThanOrEqualTo: imageView.rightAnchor)
}
rightPin?.isActive = true
// If neither the top or the bottom are pinned, center it.
if let centerY = centerY {
removeConstraint(centerY)
}
if !edge.contains(UIRectEdge.top) && !edge.contains(UIRectEdge.bottom) {
centerY = imageView.centerYAnchor.constraint(equalTo: centerYAnchor)
centerY?.isActive = true
}
// If neither the left or the right are pinned, center it.
if let centerX = centerX {
removeConstraint(centerX)
}
if !edge.contains(UIRectEdge.left) && !edge.contains(UIRectEdge.right) {
centerX = imageView.centerXAnchor.constraint(equalTo: centerXAnchor)
centerX?.isActive = true
alignCenterHorizontal()
}
}
@ -121,6 +88,7 @@ import UIKit
// Setup image.
imageView.translatesAutoresizingMaskIntoConstraints = false;
addSubview(imageView)
pinView(toSuperView: imageView)
// Setup edges constraints
if edges == nil {
@ -159,10 +127,30 @@ import UIKit
open func shouldLoadImage(withName imageName: String?, width: CGFloat) -> Bool {
// We should load a new image if there is no current image, the image names are different, the width is different, or we are using a fallback image.
guard let currentWidth = self.width else {
guard let currentWidth = self.currentImageWidth else {
return true
}
return (imageView.image == nil && imageView.animatedImage == nil) || imageName != loadingImageName || !MVMCoreGetterUtility.cgfequal(width, currentWidth) || self.isFallbackImage
return (imageView.image == nil && imageView.animatedImage == nil) || imageName != currentImageName || !MVMCoreGetterUtility.cgfequal(width, currentWidth) || isFallbackImage
}
open func shouldLoadImage(withName imageName: String?, width: CGFloat?, height: CGFloat?) -> Bool {
// We should load a new image if there is no current image, the image names are different, or we are using a fallback image.
if ((imageView.image == nil && imageView.animatedImage == nil) || imageName != currentImageName || isFallbackImage) {
return true
}
// load new image if the width is different
if let oldWidth = self.currentImageWidth, let newWidth = width, !MVMCoreGetterUtility.cgfequal(oldWidth, newWidth) {
return true
} else if (self.currentImageWidth == nil) != (width == nil) {
return true
}
// load new image if the height is different
if let oldHeight = self.currentImageHeight, let newHeight = height, !MVMCoreGetterUtility.cgfequal(oldHeight, newHeight) {
return true
} else if (self.currentImageHeight == nil) || (height == nil) {
return true
}
return false
}
// Constrains the image view to be the size provided. Used to size it to the image to fix aspect fit defect.
@ -191,43 +179,40 @@ import UIKit
imageView.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.vertical)
}
open override func updateView(_ size: CGFloat) {
super.updateView(size)
let width = size.rounded()
if let imageName = json?.optionalStringForKey("image"), shouldLoadImage(withName: imageName, width: width) {
imageView.image = nil
imageView.animatedImage = nil
loadImage(withName: imageName, format: json?.optionalStringForKey("imageFormat"), width: NSNumber(value: Double(width)), height: nil, customFallbackImage: json?.optionalStringForKey("fallbackImage"))
}
// MARK: - MVMCoreUIMoleculeViewProtocol functions
open override func setAsMolecule() {
addSizeConstraintsForAspectRatio = true
}
// MARK: - MVMCoreUIMoleculeViewProtocol functions
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) {
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
}
if let accessibilityString = json?.optionalStringForKey("accessibilityText") {
imageView.accessibilityLabel = accessibilityString
imageView.accessibilityTraits = .staticText
imageView.isAccessibilityElement = true
}
let width = json?.optionalCGFloatForKey("width") ?? imageWidth
let height = json?.optionalCGFloatForKey("height") ?? imageHeight
if let imageName = json?.optionalStringForKey("image"), shouldLoadImage(withName: imageName, width: width, height: height) {
imageView.image = nil
imageView.animatedImage = nil
loadImage(withName: imageName, format: json?.optionalStringForKey("imageFormat"), width: width as NSNumber?, height: height as NSNumber?, customFallbackImage: json?.optionalStringForKey("fallbackImage"))
}
}
// MARK: - load functions
public func loadImage(withName imageName: String?, format: String?, width: NSNumber?, height: NSNumber?, customFallbackImage: String?, completionHandler: @escaping MVMCoreGetImageBlock) {
MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in
self.loadingImageName = imageName
if let width = width {
self.width = width.cgfloat()
}
self.currentImageName = imageName
self.currentImageWidth = width?.cgfloat()
self.currentImageHeight = height?.cgfloat()
self.loadingSpinner.resumeSpinnerAfterDelay()
if let height = self.spinnerHeight {
self.loadingSpinnerHeightConstraint?.constant = height
}
let finishedLoadingBlock: MVMCoreGetImageBlock = {[weak self] (image, data, isFallbackImage) in MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in
guard let loadingImageName = self?.loadingImageName, loadingImageName == imageName else {
guard let loadingImageName = self?.currentImageName, loadingImageName == imageName else {
return
}
self?.isFallbackImage = isFallbackImage
@ -250,14 +235,14 @@ import UIKit
public func loadCroppedImage(withName imageName:
String?, width: NSNumber?, height: NSNumber?, cropRect: CGRect, flipImage: Bool, customFallbackImage: String?) {
MVMCoreDispatchUtility.performBlock(onMainThread: { [unowned self] in
self.loadingImageName = imageName
self.currentImageName = imageName
self.loadingSpinner.resumeSpinnerAfterDelay()
if let height = self.spinnerHeight {
self.loadingSpinnerHeightConstraint?.constant = height
}
MVMCoreCache.shared()?.getCroppedImage(imageName, useWidth: width != nil, widthForS7: width?.intValue ?? 0, useHeight: height != nil, heightForS7: height?.intValue ?? 0, finalRect: cropRect, flipImage: flipImage, localFallbackImageName: customFallbackImage ?? MVMCoreUIUtility.localizedImageName("fallback"), completionHandler: { [weak self] (image, data, isFallBackImage) in
MVMCoreDispatchUtility.performBlock(onMainThread: {
guard let image = image, let loadingImageName = self?.loadingImageName, loadingImageName == imageName else {
guard let image = image, let loadingImageName = self?.currentImageName, loadingImageName == imageName else {
return
}
self?.loadingSpinnerHeightConstraint?.constant = 0

View File

@ -8,6 +8,9 @@
#import "MFView.h"
@import MVMCore.Swift;
@import MVMCore.NSDictionary_MFConvenience;
#import "MVMCoreUIConstants.h"
#import "UIColor+MFConvenience.h"
@implementation MFView
@ -46,6 +49,11 @@
- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData {
self.json = json;
NSString *colorString = [json string:KeyBackgroundColor];
if (colorString) {
self.backgroundColor = [UIColor mfGetColorForHex:colorString];
}
}
@end

View File

@ -8,6 +8,7 @@
#import <UIKit/UIKit.h>
#import <MVMCoreUI/MFView.h>
#import <MVMCoreUI/MVMCoreUIViewConstrainingProtocol.h>
@class MFSizeObject;
typedef enum : NSUInteger {
@ -15,7 +16,7 @@ typedef enum : NSUInteger {
SeparatorPositionBot
} SeparatorPosition;
@interface SeparatorView : MFView
@interface SeparatorView : MFView <MVMCoreUIViewConstrainingProtocol>
@property (nullable, weak, nonatomic) NSLayoutConstraint *height;
@property (nullable, weak, nonatomic) NSLayoutConstraint *leftPin;

View File

@ -115,10 +115,6 @@
[self setAsLight];
}
}
NSString *backgroundColor = [json string:KeyBackgroundColor];
if (backgroundColor) {
self.backgroundColor = [UIColor mfGetColorForHex:backgroundColor];
}
} else {
self.hidden = YES;
}
@ -165,5 +161,15 @@
[self setNeedsLayout];
[self layoutIfNeeded];
}
#pragma mark - Molecule
- (BOOL)needsToBeConstrained {
return YES;
}
- (BOOL)copyBackgroundColor {
return NO;
}
@end

View File

@ -48,11 +48,21 @@
#pragma mark - Constraining
- (void)pinViewToSuperView:(nonnull UIView *)view {
NSDictionary *dictionary = [NSLayoutConstraint constraintPinSubviewToSuperview:view];
self.leftPin = dictionary[ConstraintLeading];
self.topPin = dictionary[ConstraintTop];
self.bottomPin = dictionary[ConstraintBot];
self.rightPin = dictionary[ConstraintTrailing];
NSLayoutConstraint *constraint = [view.leftAnchor constraintEqualToAnchor:view.superview.leftAnchor];
constraint.active = YES;
self.leftPin = constraint;
constraint = [view.topAnchor constraintEqualToAnchor:view.superview.topAnchor];
constraint.active = YES;
self.topPin = constraint;
constraint = [view.superview.rightAnchor constraintEqualToAnchor:view.rightAnchor];
constraint.active = YES;
self.rightPin = constraint;
constraint = [view.superview.bottomAnchor constraintEqualToAnchor:view.bottomAnchor];
constraint.active = YES;
self.bottomPin = constraint;
self.alignCenterPin = [view.centerXAnchor constraintEqualToAnchor:view.superview.centerXAnchor];
self.alignCenterLeftPin = [view.leftAnchor constraintGreaterThanOrEqualToAnchor:view.superview.leftAnchor];
@ -267,6 +277,9 @@
- (void)shouldSetHorizontalMargins:(BOOL)shouldSet {
self.updateViewHorizontalDefaults = shouldSet;
}
- (void)shouldSetVerticalMargins:(BOOL)shouldSet {
}
#pragma mark - MVMCoreViewProtocol
@ -312,7 +325,9 @@
[super setWithJSON:json delegateObject:delegateObject additionalData:additionalData];
if (self.molecule) {
[self.molecule setWithJSON:json delegateObject:delegateObject additionalData:additionalData];
self.backgroundColor = self.molecule.backgroundColor;
}
if (self.constrainedView && (![self.constrainedView respondsToSelector:@selector(copyBackgroundColor)] || [self.constrainedView performSelector:@selector(copyBackgroundColor)])) {
self.backgroundColor = self.constrainedView.backgroundColor;
}
}

View File

@ -89,6 +89,9 @@
// Returns if the screen size has changed.
- (BOOL)screenSizeChanged;
/// If we have new data, this is called. It calls newDataBuildScreen and sets the ui to update.
- (void)newDataBuildAndUpdate;
#pragma mark - Functions To Subclass
// This view controller should subclass this function and check the load to make sure it has all the needed data. Fills the error object if there are any errors. Returns if we should finish the load or not.
@ -131,11 +134,20 @@
// Returns an array of modules to observe for when we receive a response with JSON. Subclass this to have the ui update when the returned page types are cached.
- (nullable NSArray *)modulesToListenFor;
// The function that gets called by the notification center when the JSON is updated.
/// The function that gets called by the notification center when the JSON is updated, if we have anything we are listening for (pageTypesToListenFor, modulesToListenFor). This function also tells the screen to update (newDataBuildAndUpdate) if we received new json that we were listening for.
- (void)responseJSONUpdated:(nonnull NSNotification *)notification;
// Updates the json dictionary and the screen with the passed in dictionary. Subclass to get any custom behavior if necessary.
- (void)updateWithResponsePage:(nullable NSDictionary *)page modules:(nullable NSDictionary *)modules;
/// Sets the page on the load object. Default returns true. Return true if the page is loaded and newDataBuildAndUpdate needs to happen. Can subclass to avoid this.
- (BOOL)newPageLoaded:(nonnull NSDictionary *)page;
/// Appends to the modules on the load object. Default returns true. Return true if the modules are loaded and newDataBuildAndUpdate needs to happen. Can subclass to avoid this.
- (BOOL)newModulesLoaded:(nonnull NSDictionary *)modules;
/** Verifies that all needed modules are loaded
* @param loadObject The load data from the cache or server.
* @param error The error object passed in will be set in the case of an error.
* @return True if the calling process should continue. */
+ (BOOL)verifyRequiredModulesLoadedForLoadObject:(nullable MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nonnull *_Nonnull)error;
#pragma mark - Navigation Item, Menu, Support, Top Alert

View File

@ -93,9 +93,11 @@
#pragma mark - Functions To Subclass
- (BOOL)shouldFinishProcessingLoad:(nonnull MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nonnull *_Nonnull)error {
self.loadObject = loadObject;
self.pageType = loadObject.pageType;
return YES;
self.loadObject = loadObject;
// Verifies all modules needed are loaded.
return [MFViewController verifyRequiredModulesLoadedForLoadObject:loadObject error:error];
}
// Sets the screen to use the screen heading.
@ -213,53 +215,83 @@
// Checks for a page we are listening for.
NSArray *pageTypesListeningFor = [self pageTypesToListenFor];
NSDictionary *pages = [notification.userInfo dict:KeyPageMap];
__block NSDictionary *pageUpdated = nil;
__block BOOL newData = NO;
[pageTypesListeningFor enumerateObjectsUsingBlock:^(NSString * _Nonnull pageToListenFor, NSUInteger idx, BOOL * _Nonnull stop) {
NSDictionary *page = [pages dict:pageToListenFor];
if (page) {
pageUpdated = page;
NSString *pageType = [page string:KeyPageType];
if (page && [pageType isEqualToString:self.pageType]) {
newData = [self newPageLoaded:page];
*stop = YES;
}
}];
// Checks for modules we are listening for.
NSArray *modulesListeningFor = [self modulesToListenFor];
NSDictionary *modules = [notification.userInfo dict:KeyModuleMap];
NSDictionary *modulesReceived = [notification.userInfo dict:KeyModuleMap];
__block NSMutableDictionary *modulesUpdated = [NSMutableDictionary dictionary];
[modulesListeningFor enumerateObjectsUsingBlock:^(NSString * _Nonnull moduleToListenFor, NSUInteger idx, BOOL * _Nonnull stop) {
NSDictionary *module = [modules dict:moduleToListenFor];
NSDictionary *module = [modulesReceived dict:moduleToListenFor];
if (module) {
[modulesUpdated setObject:module forKey:moduleToListenFor];
}
}];
if (modulesUpdated.count > 0) {
newData = [self newModulesLoaded:modulesUpdated];
}
[self updateWithResponsePage:pageUpdated modules:modulesUpdated];
if (newData) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
[self newDataBuildAndUpdate];
}];
}
}
- (void)updateWithResponsePage:(nullable NSDictionary *)page modules:(nullable NSDictionary *)modules {
- (BOOL)newPageLoaded:(nonnull NSDictionary *)page {
self.loadObject.pageJSON = page;
return YES;
}
- (BOOL)newModulesLoaded:(nonnull NSDictionary *)modules {
if (self.loadObject.modulesJSON) {
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:self.loadObject.modulesJSON];
[mutableDictionary addEntriesFromDictionary:modules];
self.loadObject.modulesJSON = [NSDictionary dictionaryWithDictionary:mutableDictionary];
} else {
self.loadObject.modulesJSON = modules;
}
return YES;
}
+ (BOOL)verifyRequiredModulesLoadedForLoadObject:(nullable MVMCoreLoadObject *)loadObject error:(MVMCoreErrorObject *_Nonnull *_Nonnull)error {
if (page || modules.count > 0) {
// Check if all needed modules are loaded.
__block NSMutableArray *modulesRequired = [NSMutableArray arrayWithArray:[[MVMCoreViewControllerMappingObject sharedViewControllerMappingObject] modulesRequiredForPageType:loadObject.pageType]];
if (modulesRequired.count > 0) {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
[[loadObject.modulesJSON allKeys] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (modules) {
if (self.loadObject.modulesJSON) {
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithDictionary:self.loadObject.modulesJSON];
[mutableDictionary addEntriesFromDictionary:modules];
self.loadObject.modulesJSON = [NSDictionary dictionaryWithDictionary:mutableDictionary];
} else {
self.loadObject.modulesJSON = modules;
if (modulesRequired.count == 0) {
*stop = YES;
} else {
NSUInteger index = [modulesRequired indexOfObject:obj];
if (index != NSNotFound) {
[modulesRequired removeObjectAtIndex:index];
}
}
if (page) {
self.loadObject.pageJSON = page;
}
[self updateUI];
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
}];
if (modulesRequired.count == 0) {
return YES;
} else {
// Error, not all needed modules are loaded.
if (error) {
*error = [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorCritical] messageToLog:[modulesRequired description] code:ErrorCodeRequiredModuleNotPresent domain:ErrorDomainNative location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
}
return NO;
}
} else {
return YES;
}
}
@ -426,7 +458,7 @@
}
// Since we have new data, build stuff for the screen and update the ui once the screen is done laying out.
[self updateUI];
[self newDataBuildAndUpdate];
self.needToupdateUIOnScreenSizeChanges = YES;
@ -439,10 +471,13 @@
}
}
- (void)updateUI {
[self newDataBuildScreen];
[self.formValidator enableByValidation];
self.needToUpdateUI = YES;
- (void)newDataBuildAndUpdate {
[MVMCoreDispatchUtility performBlockOnMainThread:^{
[self newDataBuildScreen];
[self.formValidator enableByValidation];
self.needToUpdateUI = YES;
[self.view setNeedsLayout];
}];
}
- (void)didReceiveMemoryWarning {

View File

@ -64,7 +64,7 @@ import UIKit
primaryButton?.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?) -> CGFloat {
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 42
}

View File

@ -9,6 +9,7 @@
#import <UIKit/UIKit.h>
@import MVMCore.MVMCoreViewProtocol;
@class MVMCoreUIDelegateObject;
@class MVMCoreErrorObject;
@protocol MVMCoreUIMoleculeViewProtocol <NSObject, MVMCoreViewProtocol>
@ -25,11 +26,14 @@
/// For the molecule list to load more efficiently.
+ (CGFloat)estimatedHeightForRow:(nullable NSDictionary *)json;
+ (CGFloat)estimatedHeightForRow:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject;
/// Allows the molecule to set its name for reuse. Default could be moleculeName.
+ (nullable NSString *)nameForReuse:(nullable NSDictionary *)molecule delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject;
/// Can return the required modules
+ (nullable NSArray <NSString *>*)requiredModules:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject error:(MVMCoreErrorObject *_Nullable *_Nullable)error;
@end

View File

@ -0,0 +1,101 @@
//
// ModuleMolecule.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 6/25/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import UIKit
open class ModuleMolecule: ViewConstrainingView {
open var molecule: (UIView & MVMCoreUIMoleculeViewProtocol)?
open override func updateView(_ size: CGFloat) {
super.updateView(size)
molecule?.updateView(size)
}
// MARK: - MVMCoreUIMoleculeViewProtocol
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
guard let moduleName = json?.optionalStringForKey("moduleName"), let module = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else {
// Critical error
return
}
if molecule == nil {
if let moleculeView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: module, delegateObject: delegateObject, constrainIfNeeded: true) {
addSubview(moleculeView)
NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: false).values))
molecule = moleculeView
}
} else {
molecule?.setWithJSON(module, delegateObject: delegateObject, additionalData: additionalData)
}
}
open override func setAsMolecule() {
super.setAsMolecule()
updateViewHorizontalDefaults = false
molecule?.setAsMolecule?()
}
open override func reset() {
super.reset()
molecule?.reset?()
}
public override static 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
}
return MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: module)?.estimatedHeight?(forRow: module, delegateObject: delegateObject) ?? 80
}
public override static 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<>"
}
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]? {
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)) {
error?.pointee = errorObject
MVMCoreUILoggingHandler.shared()?.addError(toLog: errorObject)
}
}
if let moduleName = moduleName {
return [moduleName]
}
return nil
}
// MARK: - MVMCoreUIViewConstrainingProtocol
open override func useStandardConstraints() -> Bool {
return (molecule as? MVMCoreUIViewConstrainingProtocol)?.useStandardConstraints?() ?? true
}
open override func alignHorizontal(_ alignment: UIStackView.Alignment) {
(molecule as? MVMCoreUIViewConstrainingProtocol)?.alignHorizontal?(alignment)
}
open override func alignVertical(_ alignment: UIStackView.Alignment) {
(molecule as? MVMCoreUIViewConstrainingProtocol)?.alignVertical?(alignment)
}
open override func shouldSetHorizontalMargins(_ shouldSet: Bool) {
(molecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetHorizontalMargins?(shouldSet)
}
open override func shouldSetVerticalMargins(_ shouldSet: Bool) {
(molecule as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(shouldSet)
}
}

View File

@ -39,11 +39,15 @@ public class StackItem {
public class MoleculeStackView: ViewConstrainingView {
var contentView: UIView = MVMCoreUICommonViewsUtility.commonView()
var items: [StackItem] = []
var useStackSpacingBeforeFirstItem = false
private var moleculesShouldSetHorizontalMargins = true
private var moleculesShouldSetVerticalMargins = false
/// For setting the direction of the stack
var axis: NSLayoutConstraint.Axis = .vertical {
didSet {
updateViewHorizontalDefaults = axis == .horizontal
moleculesShouldSetHorizontalMargins = (moleculesShouldSetHorizontalMargins && axis == .vertical)
if axis != oldValue {
restack()
}
@ -107,7 +111,8 @@ public class MoleculeStackView: ViewConstrainingView {
}
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .clear
addConstrainedView(contentView)
addSubview(contentView)
pinView(toSuperView: contentView)
contentView.setContentHuggingPriority(.defaultHigh, for: .vertical)
contentView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
}
@ -133,6 +138,22 @@ public class MoleculeStackView: ViewConstrainingView {
}
}
public override func shouldSetHorizontalMargins(_ shouldSet: Bool) {
super.shouldSetHorizontalMargins(shouldSet)
moleculesShouldSetHorizontalMargins = (shouldSet && axis == .vertical)
for item in items {
(item.view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetHorizontalMargins?(moleculesShouldSetHorizontalMargins)
}
}
public override func shouldSetVerticalMargins(_ shouldSet: Bool) {
super.shouldSetVerticalMargins(shouldSet)
moleculesShouldSetVerticalMargins = false
for item in items {
(item.view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(moleculesShouldSetVerticalMargins)
}
}
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
let previousJSON = self.json
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
@ -144,10 +165,6 @@ public class MoleculeStackView: ViewConstrainingView {
items = self.items
}
self.items = []
if let colorString = json?.optionalStringForKey(KeyBackgroundColor) {
backgroundColor = .mfGet(forHex: colorString)
}
guard let molecules = json?.arrayForKey(KeyMolecules) as? [[String: Any]] else {
return
@ -163,7 +180,7 @@ public class MoleculeStackView: ViewConstrainingView {
alignVertical(ViewConstrainingView.getAlignmentFor(json?.optionalStringForKey("alignment"), defaultAlignment: .fill))
} else {
alignHorizontal(ViewConstrainingView.getAlignmentFor(json?.optionalStringForKey("alignment"), defaultAlignment: .fill))
alignVertical(.leading)
alignVertical(.fill)
}
// Adds the molecules and sets the json.
@ -171,35 +188,53 @@ public class MoleculeStackView: ViewConstrainingView {
if let moleculeJSON = map.optionalDictionaryForKey(KeyMolecule) {
var view: UIView?
if let item = items?[index] {
(item.view as? MVMCoreUIMoleculeViewProtocol)?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData)
item.update(with: moleculeJSON)
item.update(with: map)
view = item.view
(view as? MVMCoreUIMoleculeViewProtocol)?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: nil)
addStackItem(item, lastItem: index == molecules.count - 1)
} else if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) {
view = molecule
addStackItem(StackItem(with: molecule, json: map), lastItem: index == molecules.count - 1)
}
(view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetHorizontalMargins?(axis == .vertical)
(view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(false)
(view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetHorizontalMargins?(moleculesShouldSetHorizontalMargins)
(view as? MVMCoreUIViewConstrainingProtocol)?.shouldSetVerticalMargins?(moleculesShouldSetVerticalMargins)
}
}
}
public override static func name(forReuse molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? {
// This will aggregate names of molecules to make an id.
var name = ""
guard let molecules = molecule?.optionalArrayForKey(KeyMolecules) else {
return name
return "stack<>"
}
var name = "stack<"
for case let item as [AnyHashable: AnyHashable] in molecules {
if let moleculeName = item.stringOptionalWithChainOfKeysOrIndexes([KeyMolecule, KeyMoleculeName]) {
name.append(moleculeName)
if let molecule = item.optionalDictionaryForKey(KeyMolecule), let moleculeName = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule)?.name?(forReuse: molecule, delegateObject: delegateObject) ?? molecule.optionalStringForKey(KeyMoleculeName) {
name.append(moleculeName + ",")
}
}
name.append(">")
return name
}
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
// if horizontal, max, if vertical, append.
return 100
}
public override static func requiredModules(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
guard let items = json?.optionalArrayForKey(KeyMolecules) else {
return nil
}
var modules: [String] = []
for case let item as [AnyHashable: AnyHashable] in items {
if let molecule = item.optionalDictionaryForKey(KeyMolecule), let modulesForMolecule = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule)?.requiredModules?(molecule, delegateObject: delegateObject, error: error) {
modules += modulesForMolecule
}
}
return modules.count > 0 ? modules : nil
}
// MARK: - Adding to stack
/// Adds the view to the stack.
func addView(_ view: UIView, lastItem: Bool) {
@ -221,7 +256,7 @@ public class MoleculeStackView: ViewConstrainingView {
}
if axis == .vertical {
if items.count == 0 {
pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: spacing)
pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: useStackSpacingBeforeFirstItem ? spacing : stackItem.spacing ?? 0)
} else if let previousView = items.last?.view {
_ = NSLayoutConstraint(pinFirstView: previousView, toSecondView: view, withConstant: spacing, directionVertical: true)
}
@ -236,7 +271,7 @@ public class MoleculeStackView: ViewConstrainingView {
} else {
if items.count == 0 {
// First horizontal item has no spacing by default unless told otherwise.
pinView(view, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: stackItem.spacing ?? 0)
pinView(view, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: useStackSpacingBeforeFirstItem ? spacing : stackItem.spacing ?? 0)
} else if let previousView = items.last?.view {
_ = NSLayoutConstraint(pinFirstView: previousView, toSecondView: view, withConstant: spacing, directionVertical: false)
}

View File

@ -89,12 +89,7 @@ import UIKit
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)
}
let standardConstraints = (moleculeView as? MVMCoreUIViewConstrainingProtocol)?.useStandardConstraints?() ?? true
NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: standardConstraints).values))
if standardConstraints {
let constraint = contentView.heightAnchor.constraint(equalToConstant: 80)
@ -106,6 +101,12 @@ import UIKit
} else {
molecule?.setWithJSON(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData)
}
if let castView = molecule as? MVMCoreUIViewConstrainingProtocol {
let standardConstraints = castView.useStandardConstraints?() ?? true
castView.shouldSetHorizontalMargins?(!standardConstraints)
castView.shouldSetVerticalMargins?(!standardConstraints)
}
backgroundColor = molecule?.backgroundColor
// Add the caret if there is an action and it's not declared hidden.
@ -126,25 +127,26 @@ import UIKit
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 {
public static 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 estimatedHeightFor(moleculeJSON)
return height
}
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)
guard let molecule = molecule?.optionalDictionaryForKey(KeyMolecule) else {
return nil
}
return MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule)?.name?(forReuse: molecule, delegateObject: delegateObject) ?? molecule.optionalStringForKey(KeyMoleculeName)
}
public static 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

View File

@ -95,9 +95,6 @@ public class StandardFooterView: ViewConstrainingView {
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
if let colorString = json?.optionalStringForKey(KeyBackgroundColor) {
backgroundColor = .mfGet(forHex: colorString)
}
twoButtonView.setWithJSON(json?.optionalDictionaryForKey("twoButtonView"), delegateObject: delegateObject, additionalData: additionalData)
textButton.setWithJSON(json?.optionalDictionaryForKey("textButton"), delegateObject: delegateObject, additionalData: additionalData)
}

View File

@ -122,9 +122,6 @@ public class StandardHeaderView: ViewConstrainingView {
// MARK: - MVMCoreUIMoleculeViewProtocol
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
if let colorString = json?.optionalStringForKey(KeyBackgroundColor) {
backgroundColor = .mfGet(forHex: colorString)
}
if let colorString = json?.optionalStringForKey("contentColor") {
let color = UIColor.mfGet(forHex: colorString)
headlineLabel.textColor = color

View File

@ -41,11 +41,8 @@ import UIKit
// MARK: - MVMCoreUIMoleculeViewProtocol
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) {
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
}
let primaryButtonMap = json?.optionalDictionaryForKey(KeyPrimaryButton)
let secondaryButtonMap = json?.optionalDictionaryForKey(KeySecondaryButton)
let primaryButtonMap = json?.optionalDictionaryForKey("primaryButton")
let secondaryButtonMap = json?.optionalDictionaryForKey("secondaryButton")
set(primaryButtonJSON: primaryButtonMap, secondaryButtonJSON: secondaryButtonMap, delegateObject: delegateObject, additionalData: additionalData)
}

View File

@ -21,22 +21,19 @@
+ (nullable instancetype)sharedMappingObject;
/// Returns the molecule class.
- (nullable Class)getMoleculeClassWithJSON:(nonnull NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject;
- (nullable Class)getMoleculeClassWithJSON:(nonnull NSDictionary *)json;
#pragma mark - Molecule Creation
/// Creates the molecule for the given name.
- (nullable UIView <MVMCoreUIMoleculeViewProtocol>*)createMoleculeForName:(nonnull NSString *)name;
/// Creates the molecule for the molecule json. Takes into account moduleMolecule as well.
/// Creates the molecule for the molecule json.
- (nullable UIView <MVMCoreUIMoleculeViewProtocol>*)createMoleculeForJSON:(nonnull NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject;
/// Creates the molecule for the molecule json. Takes into account moduleMolecule as well. Also checks if the molecule needs to be constrained for a stack/list style situation.
/// Creates the molecule for the molecule json. Also checks if the molecule needs to be constrained for a stack/list style situation.
- (nullable UIView <MVMCoreUIMoleculeViewProtocol>*)createMoleculeForJSON:(nonnull NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject constrainIfNeeded:(BOOL)constrainIfNeeded;
#pragma mark - ModuleMolecule Helpers
#pragma mark - Convenience
/// If the molecule is a module molecule, will get the map for the molecule to load from a module. Otherwise nil.
+ (nullable NSDictionary *)getMoleculeMapForModuleMolecule:(nullable NSDictionary *)moduleMolecule delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject error:(MVMCoreErrorObject *_Nullable *_Nullable)error;
+ (nullable NSArray <NSString *>*)getRequiredModulesForJSON:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject error:(MVMCoreErrorObject *_Nullable *_Nullable)error;
+ (void)addRequiredModulesForJSON:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject moduleList:(nullable NSMutableArray <NSDictionary *>*)moduleList errorList:(nullable NSMutableArray <MVMCoreErrorObject *>*)errorList;
@end

View File

@ -10,6 +10,7 @@
@import MVMCore.MVMCoreActionUtility;
@import MVMCore.NSDictionary_MFConvenience;
@import MVMCore.MVMCoreLoadObject;
@import MVMCore.MVMCoreErrorObject;
#import "MVMCoreUIObject.h"
#import <MVMCoreUI/MVMCoreUI-Swift.h>
#import "MFTextField.h"
@ -43,7 +44,9 @@
@"listItem": MoleculeTableViewCell.class,
@"switchLineItem": SwitchLineItem.class,
@"switch": Switch.class,
@"leftRightLabelView": LeftRightLabelView.class
@"image": MFLoadImageView.class,
@"leftRightLabelView": LeftRightLabelView.class,
@"moduleMolecule": ModuleMolecule.class
} mutableCopy];
});
return mapping;
@ -54,9 +57,8 @@
return [MVMCoreActionUtility initializerClassCheck:[MVMCoreUIObject sharedInstance].moleculeMap classToVerify:self];
}
- (nullable Class)getMoleculeClassWithJSON:(nonnull NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject {
NSDictionary *moleculeJSON = [MVMCoreUIMoleculeMappingObject getMoleculeMapForModuleMolecule:json delegateObject:delegateObject error:nil] ?: json;
NSString *moleculeName = [moleculeJSON string:KeyMoleculeName];
- (nullable Class)getMoleculeClassWithJSON:(nonnull NSDictionary *)json {
NSString *moleculeName = [json string:KeyMoleculeName];
if (moleculeName) {
return [self.moleculeMapping objectForKey:moleculeName];
}
@ -82,36 +84,41 @@
}
- (nullable UIView <MVMCoreUIMoleculeViewProtocol>*)createMoleculeForJSON:(nonnull NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject constrainIfNeeded:(BOOL)constrainIfNeeded {
NSDictionary *moleculeJSON = [MVMCoreUIMoleculeMappingObject getMoleculeMapForModuleMolecule:json delegateObject:delegateObject error:nil] ?: json;
NSString *moleculeName = [moleculeJSON string:KeyMoleculeName];
NSString *moleculeName = [json string:KeyMoleculeName];
if (!moleculeName) {
return nil;
}
UIView <MVMCoreUIMoleculeViewProtocol>*molecule = [self createMoleculeForName:moleculeName];
// Check if we need to constrain this view.
UIView <MVMCoreUIViewConstrainingProtocol> *castMolecule = [molecule conformsToProtocol:@protocol(MVMCoreUIViewConstrainingProtocol)] ? (UIView <MVMCoreUIViewConstrainingProtocol> *)molecule : nil;
if (constrainIfNeeded && [castMolecule respondsToSelector:@selector(needsToBeConstrained)] && [castMolecule needsToBeConstrained]) {
molecule = [[ViewConstrainingView alloc] initWithMolecule:molecule alignment:[castMolecule respondsToSelector:@selector(alignment)] ? [castMolecule alignment] : UIStackViewAlignmentFill];
}
[molecule setWithJSON:moleculeJSON delegateObject:delegateObject additionalData:nil];
[molecule setWithJSON:json delegateObject:delegateObject additionalData:nil];
return molecule;
}
#pragma mark - ModuleMolecule Helpers
#pragma mark - Convenience
+ (nullable NSDictionary *)getMoleculeMapForModuleMolecule:(nullable NSDictionary *)moduleMolecule delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject error:(MVMCoreErrorObject *_Nullable *_Nullable)error {
NSString *moleculeName = [moduleMolecule string:KeyMoleculeName];
if ([moleculeName isEqualToString:@"moduleMolecule"]) {
NSString *moduleName = [moduleMolecule string:@"moduleName"];
NSDictionary *module = moduleName ? [delegateObject.moleculeDelegate getModuleWithName:moduleName] : nil;
if (!module && error) {
*error = [[MVMCoreErrorObject alloc] initWithTitle:nil message:[MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess] code:ErrorCodeModuleMolecule domain:ErrorDomainNative location:NSStringFromClass(self)];
}
return module;
+ (nullable NSArray <NSString *>*)getRequiredModulesForJSON:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject error:(MVMCoreErrorObject *_Nullable *_Nullable)error {
Class <MVMCoreUIMoleculeViewProtocol>theClass = [[MVMCoreUIMoleculeMappingObject sharedMappingObject] getMoleculeClassWithJSON:json];
if ([theClass respondsToSelector:@selector(requiredModules:delegateObject:error:)]) {
return [theClass requiredModules:json delegateObject:delegateObject error:error];
} else {
return nil;
}
}
+ (void)addRequiredModulesForJSON:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject moduleList:(nullable NSMutableArray <NSDictionary *>*)moduleList errorList:(nullable NSMutableArray <MVMCoreErrorObject *>*)errorList {
MVMCoreErrorObject *error = nil;
NSArray *modules = [self getRequiredModulesForJSON:json delegateObject:delegateObject error:&error];
if (modules) {
[moduleList addObjectsFromArray:modules];
}
if (error) {
[errorList addObject:error];
}
}
@end

View File

@ -21,6 +21,9 @@
/// Can be used to override any standard constraints that may be added.
- (BOOL)useStandardConstraints;
/// Determines if the constraining view will copy the background color of the delegate.
- (BOOL)copyBackgroundColor;
/// Will align if it can.
- (void)alignHorizontal:(UIStackViewAlignment)alignment;
- (void)alignVertical:(UIStackViewAlignment)alignment;

View File

@ -1,47 +0,0 @@
//
// MoleculeMappingObject+Extension.swift
// MVMCoreUI
//
// Created by Scott Pfeil on 5/23/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
//
import Foundation
public extension MVMCoreUIMoleculeMappingObject {
/// Gets the molecule, and if it belonged to a moduleMolecule, the module name or error.
static func getMoleculeJSON(for map: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> (molecule: [AnyHashable: Any]?, moduleName: String?, error: MVMCoreErrorObject?)? {
guard let map = map, let moleculeName = map.optionalStringForKey(KeyMoleculeName) else {
return nil
}
guard moleculeName == "moduleMolecule" else {
// Not a module molecule.
return (map, nil, nil)
}
guard let moduleName = map.optionalStringForKey("moduleName"),
let module = delegateObject?.moleculeDelegate?.getModuleWithName(moduleName) else {
guard let error = MVMCoreErrorObject(title: nil, message: MVMCoreGetterUtility.hardcodedString(withKey: HardcodedErrorUnableToProcess), code: CoreUIErrorCode.ErrorCodeModuleMolecule.rawValue, domain: ErrorDomainNative, location: String(describing: self)) else {
return nil
}
MVMCoreUILoggingHandler.shared()?.addError(toLog: error)
return (nil, nil, error)
}
return (module, moduleName, nil)
}
/// Gets the molecule, and if it belonged to a moduleMolecule adds the module name or error to the passed lists.
static func getMoleculeJSON(for map: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, moduleNames: inout [String]?, errors: inout [MVMCoreErrorObject]?) -> [AnyHashable: Any]? {
guard let molecule = getMoleculeJSON(for: map, delegateObject: delegateObject) else {
return nil
}
if let moduleName = molecule.moduleName {
moduleNames?.append(moduleName)
}
if let error = molecule.error {
errors?.append(error)
}
return molecule.molecule
}
}

View File

@ -9,22 +9,23 @@
import UIKit
open class MoleculeListTemplate: ThreeLayerTableViewController {
var molecules: [(molecule: [AnyHashable: Any]?, moduleName: String?, error: MVMCoreErrorObject?)]?
var moleculesInfo: [(identifier: String, class: AnyClass, molecule: [AnyHashable: Any])]?
var observer: NSKeyValueObservation?
open override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject>) -> Bool {
var shouldFinish = super.shouldFinishProcessingLoad(loadObject, error: error)
guard shouldFinish else {
return shouldFinish
open override var loadObject: MVMCoreLoadObject? {
didSet {
if loadObject != oldValue {
updateRequiredModules()
observer?.invalidate()
if let newObject = loadObject {
observer = newObject.observe(\MVMCoreLoadObject.pageJSON, options: [.old, .new]) { [weak self] (object, change) in
self?.updateRequiredModules()
}
}
}
}
if let firstError = setup()?.first {
// Don't continue if there was an error loading needed modules.
error.pointee = firstError
shouldFinish = false
}
return shouldFinish
}
open override func viewForTop() -> UIView {
guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("header"),
let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, constrainIfNeeded: true) else {
@ -43,41 +44,36 @@ open class MoleculeListTemplate: ThreeLayerTableViewController {
open override func newDataBuildScreen() {
super.newDataBuildScreen()
_ = setupMoleculeList()
setup()
registerWithTable()
}
// MARK: - table
open override func registerWithTable() {
super.registerWithTable()
guard let molecules = molecules else {
guard let moleculesInfo = moleculesInfo else {
return
}
for molecule in molecules {
if let moleculeInfo = getMoleculeInfo(with: molecule), let moleculeToRegister = moleculeInfo.name {
tableView?.register(moleculeInfo.class, forCellReuseIdentifier: moleculeToRegister)
}
for moleculeInfo in moleculesInfo {
tableView?.register(moleculeInfo.class, forCellReuseIdentifier: moleculeInfo.identifier)
}
}
open override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
guard let molecule = molecules?[indexPath.row],
let moleculeInfo = getMoleculeInfo(with: molecule),
let estimatedHeight = moleculeInfo.class.estimatedHeight else {
guard let moleculeInfo = moleculesInfo?[indexPath.row],
let estimatedHeight = moleculeInfo.class.estimatedHeight?(forRow: moleculeInfo.molecule, delegateObject: delegateObject() as? MVMCoreUIDelegateObject) else {
return 0
}
return estimatedHeight(molecule.molecule)
return estimatedHeight
}
open override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return molecules?.count ?? 0
return moleculesInfo?.count ?? 0
}
open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let molecule = molecules?[indexPath.row],
let moleculeInfo = getMoleculeInfo(with: molecule),
let moleculeName = moleculeInfo.name,
let cell = tableView.dequeueReusableCell(withIdentifier: moleculeName) else {
guard let moleculeInfo = moleculesInfo?[indexPath.row],
let cell = tableView.dequeueReusableCell(withIdentifier: moleculeInfo.identifier) else {
return UITableViewCell()
}
let delegate = delegateObject() as? MVMCoreUIDelegateObject
@ -86,7 +82,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController {
}
if let protocolCell = cell as? MVMCoreUIMoleculeViewProtocol {
protocolCell.reset?()
protocolCell.setWithJSON(molecule.molecule, delegateObject: delegate, additionalData: nil)
protocolCell.setWithJSON(moleculeInfo.molecule, delegateObject: delegate, additionalData: nil)
protocolCell.updateView(tableView.bounds.width)
}
return cell
@ -107,79 +103,64 @@ open class MoleculeListTemplate: ThreeLayerTableViewController {
}
open override func modulesToListenFor() -> [Any]? {
// Get all of the molecules that need modules.
return modulesNeeded()
return loadObject?.requestParameters?.modules
}
// MARK: - Module Molecule Handling
/// Returns the (name, class) of the molecule for the given map.
func getMoleculeInfo(with molecule: (molecule: [AnyHashable: Any]?, moduleName: String?, error: MVMCoreErrorObject?)) -> (name: String?, class: AnyClass)? {
guard let map = molecule.molecule, let moleculeName = map.optionalStringForKey(KeyMoleculeName), let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping[moleculeName] as? AnyClass else {
// MARK: - Convenience
/// Returns the (identifier, class) of the molecule for the given map.
func getMoleculeInfo(with molecule: [AnyHashable: Any]?) -> (identifier: String, class: AnyClass, molecule: [AnyHashable: Any])? {
guard let molecule = molecule,
let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: molecule),
let moleculeName = moleculeClass.name?(forReuse: molecule, delegateObject: delegateObject() as? MVMCoreUIDelegateObject) ?? molecule.optionalStringForKey(KeyMoleculeName) else {
return nil
}
if let moleculeClass = moleculeClass as? MVMCoreUIMoleculeViewProtocol.Type, let moleculeNameFunc = moleculeClass.name {
return (moleculeNameFunc(map, delegateObject() as? MVMCoreUIDelegateObject), moleculeClass)
} else {
return (moleculeName, moleculeClass)
}
return (moleculeName, moleculeClass, molecule)
}
/// Sets up the molecule list and ensures no errors loading all content.
func setupMoleculeList() -> [MVMCoreErrorObject]? {
var errors: [MVMCoreErrorObject] = []
let delegate = delegateObject() as? MVMCoreUIDelegateObject
var moleculeList: [(molecule: [AnyHashable: Any]?, moduleName: String?, error: MVMCoreErrorObject?)] = []
func getMoleculeInfoList() -> [(identifier: String, class: AnyClass, molecule: [AnyHashable: Any])]? {
var moleculeList: [(identifier: String, class: AnyClass, molecule: [AnyHashable: Any])] = []
if let molecules = loadObject?.pageJSON?.optionalArrayForKey(KeyMolecules) as? [[AnyHashable: Any]] {
for molecule in molecules {
if let object = MVMCoreUIMoleculeMappingObject.getMoleculeJSON(for: molecule, delegateObject: delegate) {
if let error = object.error {
errors.append(error)
} else {
moleculeList.append(object)
}
if let info = getMoleculeInfo(with: molecule) {
moleculeList.append(info)
}
}
}
molecules = moleculeList
return errors.count > 0 ? errors : nil
return moleculeList.count > 0 ? moleculeList : nil
}
/// Sets up the header, footer, molecule list and ensures no errors loading all content.
func setup() -> [MVMCoreErrorObject]? {
var errors: [MVMCoreErrorObject] = []
let delegate = delegateObject() as? MVMCoreUIDelegateObject
MoleculeListTemplate.addToErrorList(with: MVMCoreUIMoleculeMappingObject.getMoleculeJSON(for: loadObject?.pageJSON?.optionalDictionaryForKey("header"), delegateObject: delegate), errors: &errors)
MoleculeListTemplate.addToErrorList(with: MVMCoreUIMoleculeMappingObject.getMoleculeJSON(for: loadObject?.pageJSON?.optionalDictionaryForKey("footer"), delegateObject: delegate), errors: &errors)
if let newErrors = setupMoleculeList() {
errors.append(contentsOf: newErrors)
}
return errors.count > 0 ? errors : nil
}
static func addToErrorList(with moleculeObject: (molecule: [AnyHashable: Any]?, moduleName: String?, error: MVMCoreErrorObject?)?, errors: inout [MVMCoreErrorObject]) {
if let error = moleculeObject?.error {
errors.append(error)
}
}
static func addToModuleList(with moleculeObject: (molecule: [AnyHashable: Any]?, moduleName: String?, error: MVMCoreErrorObject?)?, moduleNames: inout [String]) {
if let moduleName = moleculeObject?.moduleName {
moduleNames.append(moduleName)
}
}
/// Gets a list of required modules
func modulesNeeded() -> [String]? {
var modules: [String] = []
let delegate = delegateObject() as? MVMCoreUIDelegateObject
MoleculeListTemplate.addToModuleList(with: MVMCoreUIMoleculeMappingObject.getMoleculeJSON(for: loadObject?.pageJSON?.optionalDictionaryForKey("header"), delegateObject: delegate), moduleNames: &modules)
MoleculeListTemplate.addToModuleList(with: MVMCoreUIMoleculeMappingObject.getMoleculeJSON(for: loadObject?.pageJSON?.optionalDictionaryForKey("footer"), delegateObject: delegate), moduleNames: &modules)
if let molecules = molecules {
func setup() {
var moleculeList: [(identifier: String, class: AnyClass, molecule: [AnyHashable: Any])] = []
if let molecules = loadObject?.pageJSON?.optionalArrayForKey(KeyMolecules) as? [[AnyHashable: Any]] {
for molecule in molecules {
MoleculeListTemplate.addToModuleList(with: molecule, moduleNames: &modules)
if let info = getMoleculeInfo(with: molecule) {
moleculeList.append(info)
}
}
}
return (modules.count > 0 ? modules : nil)
moleculesInfo = moleculeList
}
/// Adds modules from requiredModules() to the MVMCoreViewControllerMapping.requiredModules map.
open func updateRequiredModules() {
if let requiredModules = requiredModules(), let pageType = pageType {
MVMCoreViewControllerMappingObject.shared()?.addRequiredModules(toMapping: requiredModules, forPageType: pageType)
}
}
/// Gets modules required by the loadObject.pageJSON.
open func requiredModules() -> [Any]? {
let modules: NSMutableArray = []
let delegate = delegateObject() as? MVMCoreUIDelegateObject
MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("header"), delegateObject: delegate, moduleList: modules, errorList: nil)
MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("footer"), delegateObject: delegate, moduleList: modules, errorList: nil)
if let molecules = loadObject?.pageJSON?.optionalArrayForKey(KeyMolecules) as? [[AnyHashable: Any]] {
for molecule in molecules {
MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: molecule, delegateObject: delegate, moduleList: modules, errorList: nil)
}
}
return modules as? [Any]
}
}

View File

@ -9,15 +9,20 @@
import UIKit
public class MoleculeStackTemplate: ThreeLayerViewController {
var observer: NSKeyValueObservation?
public override func shouldFinishProcessingLoad(_ loadObject: MVMCoreLoadObject, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject>) -> Bool {
var shouldFinish = super.shouldFinishProcessingLoad(loadObject, error: error)
if shouldFinish, let firstError = modulesNeeded().errors?.first {
// Don't continue if there was an error loading needed modules.
error.pointee = firstError
shouldFinish = false
open override var loadObject: MVMCoreLoadObject? {
didSet {
if loadObject != oldValue {
updateRequiredModules()
observer?.invalidate()
if let newObject = loadObject {
observer = newObject.observe(\MVMCoreLoadObject.pageJSON, options: [.old, .new]) { [weak self] (object, change) in
self?.updateRequiredModules()
}
}
}
}
return shouldFinish
}
public override func spaceBetweenTopAndMiddle() -> CGFloat? {
@ -35,7 +40,10 @@ public class MoleculeStackTemplate: ThreeLayerViewController {
guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("moleculeStack") else {
return nil
}
return MoleculeStackView(withJSON: moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil)
let stack = MoleculeStackView(frame: .zero)
stack.useStackSpacingBeforeFirstItem = true
stack.setWithJSON(moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, additionalData: nil)
return stack
}
override public func viewForBottom() -> UIView? {
@ -54,23 +62,23 @@ public class MoleculeStackTemplate: ThreeLayerViewController {
}
public override func modulesToListenFor() -> [Any]? {
// Get all of the molecules that need modules.
return modulesNeeded().modules
return loadObject?.requestParameters?.modules
}
// MARK: - Module Molecule Handling
func modulesNeeded() -> (modules: [String]?, errors: [MVMCoreErrorObject]?) {
var modules: [String]? = []
var errors: [MVMCoreErrorObject]? = []
let delegate = delegateObject() as? MVMCoreUIDelegateObject
let _ = MVMCoreUIMoleculeMappingObject.getMoleculeJSON(for: loadObject?.pageJSON?.optionalDictionaryForKey("header"), delegateObject: delegate, moduleNames: &modules, errors: &errors)
let _ = MVMCoreUIMoleculeMappingObject.getMoleculeJSON(for: loadObject?.pageJSON?.optionalDictionaryForKey("footer"), delegateObject: delegate, moduleNames: &modules, errors: &errors)
if let molecules = loadObject?.pageJSON?.optionalArrayForChainOfKeysOrIndexes(["moleculeStack",KeyMolecules]) as? [[AnyHashable: Any]] {
for molecule in molecules {
let _ = MVMCoreUIMoleculeMappingObject.getMoleculeJSON(for: molecule, delegateObject: delegate, moduleNames: &modules, errors: &errors)
}
/// Adds modules from requiredModules() to the MVMCoreViewControllerMapping.requiredModules map.
open func updateRequiredModules() {
if let requiredModules = requiredModules(), let pageType = pageType {
MVMCoreViewControllerMappingObject.shared()?.addRequiredModules(toMapping: requiredModules, forPageType: pageType)
}
return (modules?.count ?? 0 > 0 ? modules : nil, errors?.count ?? 0 > 0 ? errors : nil)
}
/// Gets modules required by the loadObject.pageJSON.
open func requiredModules() -> [Any]? {
let modules: NSMutableArray = []
let delegate = delegateObject() as? MVMCoreUIDelegateObject
MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("header"), delegateObject: delegate, moduleList: modules, errorList: nil)
MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("footer"), delegateObject: delegate, moduleList: modules, errorList: nil)
MVMCoreUIMoleculeMappingObject.addRequiredModules(forJSON: loadObject?.pageJSON?.optionalDictionaryForKey("moleculeStack"), delegateObject: delegate, moduleList: modules, errorList: nil)
return modules as? [Any]
}
}