diff --git a/MVMCoreUI/Atoms/Views/Label.swift b/MVMCoreUI/Atoms/Views/Label.swift index f2792081..a5ba4be0 100644 --- a/MVMCoreUI/Atoms/Views/Label.swift +++ b/MVMCoreUI/Atoms/Views/Label.swift @@ -147,22 +147,26 @@ import MVMCore setLabel(label, withHTML: json?.optionalStringForKey("html")) - if let textColorHex = json?.optionalStringForKey(KeyTextColor), !textColorHex.isEmpty { - label.textColor = UIColor.mfGet(forHex: textColorHex) - } - if let backgroundColorHex = json?.optionalStringForKey(KeyBackgroundColor), !backgroundColorHex.isEmpty { label.backgroundColor = UIColor.mfGet(forHex: backgroundColorHex) } label.accessibilityLabel = json?.optionalStringForKey("accessibilityText") - let fontSize = json?["fontSize"] as? CGFloat + if let fontStyle = json?.optionalStringForKey("fontStyle") { + MFStyler.styleLabel(label, withStyle: fontStyle) + } else { + let fontSize = json?["fontSize"] as? CGFloat + + if let fontName = json?.optionalStringForKey("fontName") { + label.font = MFFonts.mfFont(withName: fontName, size: fontSize ?? label.font.pointSize) + } else if let fontSize = fontSize { + label.font = label.font.withSize(fontSize) + } + } - if let fontName = json?.optionalStringForKey("fontName") { - label.font = MFFonts.mfFont(withName: fontName, size: fontSize ?? label.font.pointSize) - } else if let fontSize = fontSize { - label.font = label.font.withSize(fontSize) + if let textColorHex = json?.optionalStringForKey(KeyTextColor), !textColorHex.isEmpty { + label.textColor = UIColor.mfGet(forHex: textColorHex) } if let attributes = json?.arrayForKey("attributes"), let labelText = label.text { @@ -190,18 +194,25 @@ import MVMCore attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range) } case "font": - let fontSize = attribute["size"] as? CGFloat - var font: UIFont? - - if let fontName = attribute.optionalStringForKey("name") { - font = MFFonts.mfFont(withName: fontName, size: fontSize ?? label.font.pointSize) - } else if let fontSize = fontSize { - font = label.font.withSize(fontSize) - } - - if let font = font { + if let fontStyle = attribute.optionalStringForKey("style") { + let styles = MFStyler.styleGetAttributedString("0", withStyle: fontStyle) attributedString.removeAttribute(.font, range: range) - attributedString.addAttribute(.font, value: 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 + var font: UIFont? + + if let fontName = attribute.optionalStringForKey("name") { + font = MFFonts.mfFont(withName: fontName, size: fontSize ?? label.font.pointSize) + } else if let fontSize = fontSize { + font = label.font.withSize(fontSize) + } + + if let font = font { + attributedString.removeAttribute(.font, range: range) + attributedString.addAttribute(.font, value: font, range: range) + } } default: continue @@ -308,7 +319,7 @@ import MVMCore } public func setAsMolecule() { - setContentHuggingPriority(.required, for: .vertical) + styleB2(true) } public func needsToBeConstrained() -> Bool { diff --git a/MVMCoreUI/Atoms/Views/LabelView.h b/MVMCoreUI/Atoms/Views/LabelView.h index a6b1948c..8a0eb310 100644 --- a/MVMCoreUI/Atoms/Views/LabelView.h +++ b/MVMCoreUI/Atoms/Views/LabelView.h @@ -15,9 +15,4 @@ // Customize the label. @property (nullable, weak, nonatomic) Label *label; -// Change the alignment of the label -- (void)alignLeft; -- (void)alignCenter; -- (void)alignRight; - @end diff --git a/MVMCoreUI/Atoms/Views/LabelView.m b/MVMCoreUI/Atoms/Views/LabelView.m index e0817374..d13ff39a 100644 --- a/MVMCoreUI/Atoms/Views/LabelView.m +++ b/MVMCoreUI/Atoms/Views/LabelView.m @@ -11,11 +11,6 @@ #import @interface LabelView () -// Change to customize. -@property (strong, nonatomic) NSLayoutConstraint *alignCenterPin; -@property (strong, nonatomic) NSLayoutConstraint *alignCenterLeftPin; -@property (strong, nonatomic) NSLayoutConstraint *alignCenterRightPin; - // Sets up the view. - (void)setupView; @@ -36,39 +31,7 @@ Label *label = [Label commonLabelB2:YES]; [self addSubview:label]; self.label = label; - - // Align left and right constants. - NSLayoutConstraint *leftPin = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]; - self.leftPin = leftPin; - leftPin.active = YES; - - NSLayoutConstraint *topPin = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]; - topPin.priority = 999; - self.topPin = topPin; - topPin.active = YES; - - NSLayoutConstraint *bottomPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:label attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]; - self.bottomPin = bottomPin; - bottomPin.active = YES; - - NSLayoutConstraint *rightPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:label attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]; - rightPin.priority = 999; - self.rightPin = rightPin; - rightPin.active = YES; - - // Center alignments - NSLayoutConstraint *alignCenter = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]; - self.alignCenterPin = alignCenter; - alignCenter.active = YES; - - NSLayoutConstraint *centerLeftPin = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]; - self.alignCenterLeftPin = centerLeftPin; - centerLeftPin.active = YES; - - NSLayoutConstraint *centerRightPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:label attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]; - self.alignCenterRightPin = centerRightPin; - centerRightPin.active = YES; - + [self pinViewToSuperView:label]; [self alignLeft]; } } @@ -79,42 +42,20 @@ } - (void)alignLeft { - self.alignCenterPin.active = NO; - self.alignCenterLeftPin.active = NO; - self.alignCenterRightPin.active = NO; - self.leftPin.active = YES; - self.rightPin.active = YES; + [super alignLeft]; self.label.textAlignment = NSTextAlignmentLeft; } -- (void)alignCenter { - self.alignCenterPin.active = YES; - self.alignCenterLeftPin.active = YES; - self.alignCenterRightPin.active = YES; - self.leftPin.active = NO; - self.rightPin.active = NO; +- (void)alignCenterHorizontal { + [super alignCenterHorizontal]; self.label.textAlignment = NSTextAlignmentCenter; } - (void)alignRight { - self.alignCenterPin.active = NO; - self.alignCenterLeftPin.active = NO; - self.alignCenterRightPin.active = NO; - self.leftPin.active = YES; - self.rightPin.active = YES; + [super alignRight]; self.label.textAlignment = NSTextAlignmentRight; } -- (void)setLeftPinConstant:(CGFloat)constant { - [super setLeftPinConstant:constant]; - self.alignCenterLeftPin.constant = constant; -} - -- (void)setRightPinConstant:(CGFloat)constant { - [super setRightPinConstant:constant]; - self.alignCenterRightPin.constant = constant; -} - - (void)resetConstraints { [super resetConstraints]; [self alignLeft]; diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUITextFieldView.h b/MVMCoreUI/Atoms/Views/MVMCoreUITextFieldView.h index 757b937e..a70804b6 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUITextFieldView.h +++ b/MVMCoreUI/Atoms/Views/MVMCoreUITextFieldView.h @@ -15,9 +15,4 @@ + (nullable instancetype)createWithDelegate:(nullable id )delegate; -// Change the alignment of the label -- (void)alignLeft; -- (void)alignCenter; -- (void)alignRight; - @end diff --git a/MVMCoreUI/Atoms/Views/MVMCoreUITextFieldView.m b/MVMCoreUI/Atoms/Views/MVMCoreUITextFieldView.m index 4e3b2f72..f43b6356 100644 --- a/MVMCoreUI/Atoms/Views/MVMCoreUITextFieldView.m +++ b/MVMCoreUI/Atoms/Views/MVMCoreUITextFieldView.m @@ -10,9 +10,6 @@ #import "NSLayoutConstraint+MFConvenience.h" @interface MVMCoreUITextFieldView () -@property (weak, nonatomic) NSLayoutConstraint *alignCenterPin; -@property (weak, nonatomic) NSLayoutConstraint *alignCenterLeftPin; -@property (weak, nonatomic) NSLayoutConstraint *alignCenterRightPin; @end @implementation MVMCoreUITextFieldView @@ -31,74 +28,11 @@ mfTextField.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:mfTextField]; self.mvmTextField = mfTextField; - - NSLayoutConstraint *leftPin = [NSLayoutConstraint constraintWithItem:mfTextField attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]; - self.leftPin = leftPin; - leftPin.active = YES; - - NSLayoutConstraint *topPin = [NSLayoutConstraint constraintWithItem:mfTextField attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]; - self.topPin = topPin; - topPin.active = YES; - - NSLayoutConstraint *bottomPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:mfTextField attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]; - self.bottomPin = bottomPin; - bottomPin.active = YES; - - NSLayoutConstraint *rightPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:mfTextField attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]; - self.rightPin = rightPin; - rightPin.active = YES; - - // Center alignments - NSLayoutConstraint *alignCenter = [NSLayoutConstraint constraintWithItem:mfTextField attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]; - self.alignCenterPin = alignCenter; - alignCenter.active = YES; - - NSLayoutConstraint *centerLeftPin = [NSLayoutConstraint constraintWithItem:mfTextField attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]; - self.alignCenterLeftPin = centerLeftPin; - centerLeftPin.active = YES; - - NSLayoutConstraint *centerRightPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:mfTextField attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]; - self.alignCenterRightPin = centerRightPin; - centerRightPin.active = YES; - + [self pinViewToSuperView:mfTextField]; [self alignLeft]; } } -- (void)alignLeft { - self.alignCenterPin.active = NO; - self.alignCenterLeftPin.active = NO; - self.alignCenterRightPin.active = NO; - self.leftPin.active = YES; - self.rightPin.active = YES; -} - -- (void)alignCenter { - self.alignCenterPin.active = YES; - self.alignCenterLeftPin.active = YES; - self.alignCenterRightPin.active = YES; - self.leftPin.active = NO; - self.rightPin.active = NO; -} - -- (void)alignRight { - self.alignCenterPin.active = NO; - self.alignCenterLeftPin.active = NO; - self.alignCenterRightPin.active = NO; - self.leftPin.active = YES; - self.rightPin.active = YES; -} - -- (void)setLeftPinConstant:(CGFloat)constant { - self.leftPin.constant = constant; - self.alignCenterLeftPin.constant = constant; -} - -- (void)setRightPinConstant:(CGFloat)constant { - self.rightPin.constant = constant; - self.alignCenterRightPin.constant = constant; -} - - (void)resetConstraints { [super resetConstraints]; [self alignLeft]; diff --git a/MVMCoreUI/Atoms/Views/TextButtonView.h b/MVMCoreUI/Atoms/Views/TextButtonView.h index 0d4a6995..49d0c9a0 100644 --- a/MVMCoreUI/Atoms/Views/TextButtonView.h +++ b/MVMCoreUI/Atoms/Views/TextButtonView.h @@ -20,11 +20,6 @@ // inits with two buttons. - (nullable instancetype)initWithTwoButtons; -// Change the alignment of the button. Default is left. -- (void)alignLeft; -- (void)alignCenter; -- (void)alignRight; - // To add dotted underline below text button, in case of one button - (void)addDotLineBelowButton; diff --git a/MVMCoreUI/Atoms/Views/TextButtonView.m b/MVMCoreUI/Atoms/Views/TextButtonView.m index 7c9f5e23..9e6435e0 100644 --- a/MVMCoreUI/Atoms/Views/TextButtonView.m +++ b/MVMCoreUI/Atoms/Views/TextButtonView.m @@ -14,10 +14,6 @@ #import @interface TextButtonView () - -@property (weak, nonatomic) NSLayoutConstraint *alignCenterPin; -@property (weak, nonatomic) NSLayoutConstraint *alignCenterLeftPin; -@property (weak, nonatomic) NSLayoutConstraint *alignCenterRightPin; @property (strong, nonatomic) UIView *dotLine; // Sets up the view. @@ -78,37 +74,7 @@ button.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:button]; self.textButton = button; - - // Align left and right constants. - NSLayoutConstraint *leftPin = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]; - self.leftPin = leftPin; - leftPin.active = YES; - - NSLayoutConstraint *topPin = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]; - self.topPin = topPin; - topPin.active = YES; - - NSLayoutConstraint *bottomPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:button attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]; - self.bottomPin = bottomPin; - bottomPin.active = YES; - - NSLayoutConstraint *rightPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:button attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]; - self.rightPin = rightPin; - rightPin.active = YES; - - // Center alignments - NSLayoutConstraint *alignCenter = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]; - self.alignCenterPin = alignCenter; - alignCenter.active = YES; - - NSLayoutConstraint *centerLeftPin = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0]; - self.alignCenterLeftPin = centerLeftPin; - centerLeftPin.active = YES; - - NSLayoutConstraint *centerRightPin = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:button attribute:NSLayoutAttributeRight multiplier:1.0 constant:0]; - self.alignCenterRightPin = centerRightPin; - centerRightPin.active = YES; - + [self pinViewToSuperView:button]; [self alignLeft]; } } @@ -154,40 +120,6 @@ return self; } -- (void)alignLeft { - self.alignCenterPin.active = NO; - self.alignCenterLeftPin.active = NO; - self.alignCenterRightPin.active = YES; - self.leftPin.active = YES; - self.rightPin.active = NO; -} - -- (void)alignCenter { - self.alignCenterPin.active = YES; - self.alignCenterLeftPin.active = YES; - self.alignCenterRightPin.active = YES; - self.leftPin.active = NO; - self.rightPin.active = NO; -} - -- (void)alignRight { - self.alignCenterPin.active = NO; - self.alignCenterLeftPin.active = YES; - self.alignCenterRightPin.active = NO; - self.leftPin.active = NO; - self.rightPin.active = YES; -} - -- (void)setLeftPinConstant:(CGFloat)constant { - [super setLeftPinConstant:constant]; - self.alignCenterLeftPin.constant = constant; -} - -- (void)setRightPinConstant:(CGFloat)constant { - [super setRightPinConstant:constant]; - self.alignCenterRightPin.constant = constant; -} - - (void)resetConstraints { [super resetConstraints]; [self alignLeft]; diff --git a/MVMCoreUI/Atoms/Views/ViewConstrainingView.h b/MVMCoreUI/Atoms/Views/ViewConstrainingView.h index 6b97f891..371e137b 100644 --- a/MVMCoreUI/Atoms/Views/ViewConstrainingView.h +++ b/MVMCoreUI/Atoms/Views/ViewConstrainingView.h @@ -17,9 +17,24 @@ @property (nullable, strong, nonatomic) NSLayoutConstraint *topPin; @property (nullable, strong, nonatomic) NSLayoutConstraint *bottomPin; +@property (nullable, strong, nonatomic) NSLayoutConstraint *alignCenterPin; +@property (nullable, strong, nonatomic) NSLayoutConstraint *alignCenterLeftPin; +@property (nullable, strong, nonatomic) NSLayoutConstraint *alignCenterRightPin; + +@property (nullable, strong, nonatomic) NSLayoutConstraint *alignCenterVerticalPin; +@property (nullable, strong, nonatomic) NSLayoutConstraint *alignCenterTopPin; +@property (nullable, strong, nonatomic) NSLayoutConstraint *alignCenterBottomPin; + +@property (nullable, strong, nonatomic) NSLayoutConstraint *leftPinLow; +@property (nullable, strong, nonatomic) NSLayoutConstraint *rightPinLow; +@property (nullable, strong, nonatomic) NSLayoutConstraint *topPinLow; +@property (nullable, strong, nonatomic) NSLayoutConstraint *bottomPinLow; + + // In updateView, will set horizontal padding to default if set to YES. @property (nonatomic) BOOL updateViewHorizontalDefaults; + // Returns an empty view + (nonnull ViewConstrainingView *)emptyView; @@ -43,6 +58,7 @@ // Pins all edges to its super. 0 constant - (void)pinToSuperView; +- (void)pinViewToSuperView:(nonnull UIView *)view; // Resets all the constraints to default. - (void)resetConstraints; @@ -53,4 +69,17 @@ // Add a view to be constrained in this view. - (void)addConstrainedView:(nonnull UIView *)view; +// Change the alignment of the label +- (void)alignLeft; +- (void)alignCenterHorizontal; +- (void)alignRight; +- (void)alignFillHorizontal; +- (void)alignTop; +- (void)alignCenterVertical; +- (void)alignBottom; +- (void)alignFillVertical; + +/// Convenience function for getting the alignment from a map. ++ (UIStackViewAlignment)getAlignmentForString:(nullable NSString *)alignmentString defaultAlignment:(UIStackViewAlignment)defaultAlignment; + @end diff --git a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m index 2830f19b..a7412b93 100644 --- a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m +++ b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m @@ -45,12 +45,42 @@ return constrainingView; } -- (void)pinToSuperView { - NSDictionary *dictionary = [NSLayoutConstraint constraintPinSubviewToSuperview:self]; +#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]; + + self.alignCenterPin = [view.centerXAnchor constraintEqualToAnchor:view.superview.centerXAnchor]; + self.alignCenterLeftPin = [view.leftAnchor constraintGreaterThanOrEqualToAnchor:view.superview.leftAnchor]; + self.alignCenterRightPin = [view.superview.rightAnchor constraintGreaterThanOrEqualToAnchor:view.rightAnchor]; + + self.alignCenterVerticalPin = [view.centerYAnchor constraintEqualToAnchor:view.superview.centerYAnchor]; + self.alignCenterTopPin = [view.topAnchor constraintGreaterThanOrEqualToAnchor:view.superview.topAnchor]; + self.alignCenterBottomPin = [view.superview.bottomAnchor constraintGreaterThanOrEqualToAnchor:view.bottomAnchor]; + + self.leftPinLow = [view.leftAnchor constraintEqualToAnchor:view.superview.leftAnchor]; + self.leftPinLow.priority = 200; + self.leftPinLow.active = YES; + + self.topPinLow = [view.topAnchor constraintEqualToAnchor:view.superview.topAnchor]; + self.topPinLow.priority = 200; + self.topPinLow.active = YES; + + self.rightPinLow = [view.superview.rightAnchor constraintEqualToAnchor:view.superview.rightAnchor]; + self.rightPinLow.priority = 200; + self.rightPinLow.active = YES; + + self.bottomPinLow = [view.superview.bottomAnchor constraintEqualToAnchor:view.superview.bottomAnchor]; + self.bottomPinLow.priority = 200; + self.bottomPinLow.active = YES; +} + +- (void)pinToSuperView { + [self pinViewToSuperView:self]; } - (void)setPinConstantsWithInsets:(UIEdgeInsets)insets { @@ -66,18 +96,26 @@ - (void)setLeftPinConstant:(CGFloat)constant { self.leftPin.constant = constant; + self.alignCenterLeftPin.constant = constant; + self.leftPinLow.constant = constant; } - (void)setRightPinConstant:(CGFloat)constant { self.rightPin.constant = constant; + self.alignCenterRightPin.constant = constant; + self.rightPinLow.constant = constant; } - (void)setTopPinConstant:(CGFloat)constant { self.topPin.constant = constant; + self.alignCenterTopPin.constant = constant; + self.topPinLow.constant = constant; } - (void)setBottomPinConstant:(CGFloat)constant { self.bottomPin.constant = constant; + self.alignCenterBottomPin.constant = constant; + self.bottomPinLow.constant = constant; } - (void)show { @@ -100,30 +138,138 @@ view.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:view]; self.constrainedView = view; - - NSLayoutRelation leftRelation; - if (alignment == UIStackViewAlignmentFill || alignment == UIStackViewAlignmentLeading || alignment == UIStackViewAlignmentFirstBaseline) { - leftRelation = NSLayoutRelationEqual; - } else { - leftRelation = NSLayoutRelationGreaterThanOrEqual; - } - NSLayoutRelation rightRelation; - if (alignment == UIStackViewAlignmentFill || alignment == UIStackViewAlignmentTrailing || alignment == UIStackViewAlignmentLastBaseline) { - rightRelation = NSLayoutRelationEqual; - } else { - rightRelation = NSLayoutRelationGreaterThanOrEqual; - } - NSDictionary *dictionary = [NSLayoutConstraint constraintPinSubview:view topRelation:NSLayoutRelationEqual bottomRelation:NSLayoutRelationEqual leftRelation:leftRelation rightRelation:rightRelation]; - self.leftPin = dictionary[ConstraintLeading]; - self.topPin = dictionary[ConstraintTop]; - self.bottomPin = dictionary[ConstraintBot]; - self.rightPin = dictionary[ConstraintTrailing]; + [self pinViewToSuperView:view]; + [self alignHorizontal:alignment]; } - (void)addConstrainedView:(nonnull UIView *)view { [self addConstrainedView:view alignment:UIStackViewAlignmentFill]; } +- (void)alignLeft { + self.alignCenterPin.active = NO; + self.alignCenterLeftPin.active = NO; + self.alignCenterRightPin.active = YES; + self.leftPin.active = YES; + self.rightPin.active = NO; +} + +- (void)alignCenterHorizontal { + self.alignCenterPin.active = YES; + self.alignCenterLeftPin.active = YES; + self.alignCenterRightPin.active = YES; + self.leftPin.active = NO; + self.rightPin.active = NO; +} + +- (void)alignRight { + self.alignCenterPin.active = NO; + self.alignCenterLeftPin.active = YES; + self.alignCenterRightPin.active = NO; + self.leftPin.active = NO; + self.rightPin.active = YES; +} + +- (void)alignFillHorizontal { + self.alignCenterPin.active = NO; + self.alignCenterLeftPin.active = NO; + self.alignCenterRightPin.active = NO; + self.leftPin.active = YES; + self.rightPin.active = YES; +} + +- (void)alignTop { + self.alignCenterVerticalPin.active = NO; + self.alignCenterTopPin.active = NO; + self.alignCenterBottomPin.active = YES; + self.topPin.active = YES; + self.bottomPin.active = NO; +} + +- (void)alignCenterVertical { + self.alignCenterVerticalPin.active = YES; + self.alignCenterTopPin.active = YES; + self.alignCenterBottomPin.active = YES; + self.topPin.active = NO; + self.bottomPin.active = NO; +} + +- (void)alignBottom { + self.alignCenterVerticalPin.active = NO; + self.alignCenterTopPin.active = YES; + self.alignCenterBottomPin.active = NO; + self.topPin.active = NO; + self.bottomPin.active = YES; +} + +- (void)alignFillVertical { + self.alignCenterVerticalPin.active = NO; + self.alignCenterTopPin.active = NO; + self.alignCenterBottomPin.active = NO; + self.topPin.active = YES; + self.bottomPin.active = YES; +} + ++ (UIStackViewAlignment)getAlignmentForString:(nullable NSString *)alignmentString defaultAlignment:(UIStackViewAlignment)defaultAlignment { + if ([alignmentString isEqualToString:@"leading"]) { + return UIStackViewAlignmentLeading; + } else if ([alignmentString isEqualToString:@"trailing"]) { + return UIStackViewAlignmentTrailing; + } else if ([alignmentString isEqualToString:@"center"]) { + return UIStackViewAlignmentCenter; + } else if ([alignmentString isEqualToString:@"fill"]) { + return UIStackViewAlignmentFill; + } else { + return defaultAlignment; + } +} + +#pragma mark - MVMCoreUIViewConstrainingProtocol + +- (void)alignHorizontal:(UIStackViewAlignment)alignment { + switch (alignment) { + case UIStackViewAlignmentCenter: + [self alignCenterHorizontal]; + break; + case UIStackViewAlignmentLeading: + [self alignLeft]; + break; + case UIStackViewAlignmentTrailing: + [self alignRight]; + break; + case UIStackViewAlignmentFill: + [self alignFillHorizontal]; + break; + default: + break; + } +} + +- (void)alignVertical:(UIStackViewAlignment)alignment { + switch (alignment) { + case UIStackViewAlignmentCenter: + [self alignCenterVertical]; + break; + case UIStackViewAlignmentLeading: + [self alignTop]; + break; + case UIStackViewAlignmentTrailing: + [self alignBottom]; + break; + case UIStackViewAlignmentFill: + [self alignFillVertical]; + break; + default: + break; + } +} + +- (void)shouldSetHorizontalMargins:(BOOL)shouldSet { + self.updateViewHorizontalDefaults = shouldSet; +} + +#pragma mark - MVMCoreViewProtocol + - (void)setupView { [super setupView]; self.translatesAutoresizingMaskIntoConstraints = NO; @@ -145,14 +291,14 @@ } } +#pragma mark - MVMCoreUIMoleculeViewProtocol + - (void)reset { if ([self.constrainedView respondsToSelector:@selector(reset)]) { [self.constrainedView performSelector:@selector(reset)]; } } -#pragma mark - MVMCoreUIMoleculeViewProtocol - - (void)setAsMolecule { self.updateViewHorizontalDefaults = YES; } diff --git a/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.h b/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.h index 0d0d281c..3d615da7 100644 --- a/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.h +++ b/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.h @@ -55,7 +55,7 @@ extern NSString * _Nonnull const ConstraintWidth; + (nullable NSDictionary *)constraintPinView:(nonnull UIView*)view heightConstraint:(BOOL)pinHeight heightConstant:(CGFloat)heightConstant widthConstraint:(BOOL)pinWidth widthConstant:(CGFloat)widthConstant; // pins 2 views in same hierarchy -+(nullable NSLayoutConstraint *)constraintPinFirstView :(nonnull UIView*)firstView toSecondView :(nonnull UIView*)secondView withConstant :(CGFloat)constant directionVertical :(BOOL)directionVertical; ++(nonnull NSLayoutConstraint *)constraintPinFirstView :(nonnull UIView*)firstView toSecondView :(nonnull UIView*)secondView withConstant :(CGFloat)constant directionVertical :(BOOL)directionVertical; +(nullable NSDictionary *)constraintAlignView :(nonnull UIView *)firstView toSecondView :(nonnull UIView *)secondView alignX :(BOOL)alignX alignY :(BOOL)alignY; diff --git a/MVMCoreUI/Molecules/ButtonView.swift b/MVMCoreUI/Molecules/ButtonView.swift index 3e863a8e..4596164c 100644 --- a/MVMCoreUI/Molecules/ButtonView.swift +++ b/MVMCoreUI/Molecules/ButtonView.swift @@ -10,9 +10,6 @@ import UIKit @objcMembers open class ButtonView: ViewConstrainingView { open var primaryButton: PrimaryButton? = PrimaryButton.button() - open var alignCenterPin: NSLayoutConstraint? - open var alignCenterLeftPin: NSLayoutConstraint? - open var alignCenterRightPin: NSLayoutConstraint? // MARK: - Inits public init() { @@ -53,7 +50,7 @@ import UIKit open override func setupView() { super.setupView() setupButton() - alignCenter() + alignCenterHorizontal() } // MARK: - MVMCoreUIMoleculeViewProtocol @@ -75,56 +72,9 @@ import UIKit func setupButton() { if let primaryButton = primaryButton, !subviews.contains(primaryButton) { addSubview(primaryButton) - setupConstraints(forView: primaryButton) + pinView(toSuperView: primaryButton) } } - - func setupConstraints(forView view: UIView) { - leftPin = view.leftAnchor.constraint(equalTo: leftAnchor) - topPin = view.topAnchor.constraint(equalTo: topAnchor) - rightPin = rightAnchor.constraint(equalTo: view.rightAnchor) - bottomPin = bottomAnchor.constraint(equalTo: view.bottomAnchor) - leftPin?.isActive = true - topPin?.isActive = true - rightPin?.isActive = true - bottomPin?.isActive = true - - alignCenterPin = view.centerXAnchor.constraint(equalTo: centerXAnchor) - alignCenterLeftPin = view.leftAnchor.constraint(greaterThanOrEqualTo: leftAnchor) - alignCenterRightPin = rightAnchor.constraint(greaterThanOrEqualTo: view.rightAnchor) - } - - open func alignLeft() { - alignCenterPin?.isActive = false - alignCenterLeftPin?.isActive = false - alignCenterRightPin?.isActive = true - leftPin?.isActive = true - rightPin?.isActive = false - } - - open func alignCenter() { - alignCenterPin?.isActive = true - alignCenterLeftPin?.isActive = true - alignCenterRightPin?.isActive = true - leftPin?.isActive = false - rightPin?.isActive = false - } - - open func alignRight() { - alignCenterPin?.isActive = false - alignCenterLeftPin?.isActive = true - alignCenterRightPin?.isActive = false - leftPin?.isActive = false - rightPin?.isActive = true - } - - open func alignFill() { - alignCenterPin?.isActive = false - alignCenterLeftPin?.isActive = false - alignCenterRightPin?.isActive = false - leftPin?.isActive = true - rightPin?.isActive = true - } open override func setLeftPinConstant(_ constant: CGFloat) { super.setLeftPinConstant(constant) diff --git a/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h b/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h index 962b0c0c..9140245e 100644 --- a/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h +++ b/MVMCoreUI/Molecules/MVMCoreUIMoleculeViewProtocol.h @@ -27,6 +27,9 @@ /// For the molecule list to load more efficiently. + (CGFloat)estimatedHeightForRow:(nullable NSDictionary *)json; +/// Allows the molecule to set its name for reuse. Default could be moleculeName. ++ (nullable NSString *)nameForReuse:(nullable NSDictionary *)molecule delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject; + @end diff --git a/MVMCoreUI/Molecules/MoleculeStackView.swift b/MVMCoreUI/Molecules/MoleculeStackView.swift index 5f7f8957..7c72cf09 100644 --- a/MVMCoreUI/Molecules/MoleculeStackView.swift +++ b/MVMCoreUI/Molecules/MoleculeStackView.swift @@ -8,10 +8,82 @@ import UIKit -public class MoleculeStackView: MFView { - var spacingBlock: ((Any) -> UIEdgeInsets)? - var moleculesArray: [UIView]? - var useMargins: Bool = false +public class StackItem { + var view: UIView + var spacing: CGFloat? + var percentage: Int? + var verticalAlignment: UIStackView.Alignment? + var horizontalAlignment: UIStackView.Alignment? + + init(with view: UIView) { + self.view = view + } + + init(with view: UIView, json: [AnyHashable: Any]) { + self.view = view + update(with: json) + } + + func update(with json: [AnyHashable: Any]) { + spacing = json.optionalCGFloatForKey("spacing") + percentage = json["percent"] as? Int + if let alignment = json.optionalStringForKey("verticalAlignment") { + verticalAlignment = ViewConstrainingView.getAlignmentFor(alignment, defaultAlignment: .fill) + } + if let alignment = json.optionalStringForKey("horizontalAlignment") { + horizontalAlignment = ViewConstrainingView.getAlignmentFor(alignment, defaultAlignment: .fill) + } + } +} + +public class MoleculeStackView: ViewConstrainingView { + var contentView: UIView = MVMCoreUICommonViewsUtility.commonView() + var items: [StackItem] = [] + + /// For setting the direction of the stack + var axis: NSLayoutConstraint.Axis = .vertical { + didSet { + updateViewHorizontalDefaults = axis == .horizontal + if axis != oldValue { + restack() + } + } + } + + /// The spacing to use between each item in the stack. + var spacing: CGFloat = 16 { + didSet { + if spacing != oldValue { + restack() + } + } + } + + // MARK: - Helpers + public func setAxisWithJSON(_ json: [AnyHashable: Any]?) { + switch json?.optionalStringForKey("axis") { + case "horizontal": + axis = .horizontal + default: + axis = .vertical + } + } + + public func pinView(_ view: UIView, toView: UIView, attribute: NSLayoutConstraint.Attribute, relation: NSLayoutConstraint.Relation, priority: UILayoutPriority, constant: CGFloat) { + let constraint = NSLayoutConstraint(item: view, attribute: attribute, relatedBy: relation, toItem: toView, attribute: attribute, multiplier: 1.0, constant: constant) + constraint.priority = priority + constraint.isActive = true + } + + /// Restacks the existing items. + func restack() { + MVMCoreUIStackableViewController.remove(contentView.subviews) + let stackItems = items + items.removeAll() + for (index, item) in stackItems.enumerated() { + addStackItem(item, lastItem: index == stackItems.count - 1) + } + } // MARK: - Inits public override init(frame: CGRect) { @@ -23,11 +95,6 @@ public class MoleculeStackView: MFView { setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) } - public convenience init(withJSON json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, spacingBlock: ((Any) -> UIEdgeInsets)?) { - self.init(withJSON: json, delegateObject: delegateObject, additionalData: nil) - self.spacingBlock = spacingBlock - } - public required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -35,47 +102,153 @@ public class MoleculeStackView: MFView { // MARK: - MFViewProtocol public override func setupView() { super.setupView() + guard contentView.superview == nil else { + return + } translatesAutoresizingMaskIntoConstraints = false backgroundColor = .clear - self.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical) - self.setContentCompressionResistancePriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical) + addConstrainedView(contentView) + contentView.setContentHuggingPriority(.defaultHigh, for: .vertical) + contentView.setContentHuggingPriority(.defaultHigh, for: .horizontal) } public override func updateView(_ size: CGFloat) { super.updateView(size) - for view in subviews { - if let mvmView = view as? MVMCoreViewProtocol { - mvmView.updateView(size) - } + for item in items { + (item.view as? MVMCoreViewProtocol)?.updateView(size) } } // MARK: - MVMCoreUIMoleculeViewProtocol + public override func setAsMolecule() { + updateViewHorizontalDefaults = false + } + + public override func reset() { + backgroundColor = .clear + for item in items { + if let view = item.view as? MVMCoreUIMoleculeViewProtocol { + view.reset?() + } + } + } + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + let previousJSON = self.json super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + MVMCoreUIStackableViewController.remove(contentView.subviews) + + // If the items in the stack are the same, just update previous items instead of re-allocating. + var items: [StackItem]? + if MoleculeStackView.name(forReuse: previousJSON, delegateObject: delegateObject) == MoleculeStackView.name(forReuse: json, delegateObject: delegateObject) { + 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 } - // Create the molecules and set the json. - var moleculesArray = [] as [UIView] - for moleculeJSON in molecules { - if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: true) { - moleculesArray.append(molecule) - } - } + // Sets the stack attributes + setAxisWithJSON(json) + spacing = json?.optionalCGFloatForKey("spacing") ?? 16 - guard moleculesArray.count > 0 else { - return - } - - if let spacingBlock = spacingBlock { - MVMCoreUIStackableViewController.populateView(self, withUIArray: moleculesArray, useMargins: useMargins, withSpacingBlock: spacingBlock) + // Set the alignment for the stack in the containing view. The json driven value is for the axis direction alignment. + if axis == .vertical { + alignHorizontal(.fill) + alignVertical(ViewConstrainingView.getAlignmentFor(json?.optionalStringForKey("alignment"), defaultAlignment: .fill)) } else { - let separation = json?.optionalCGFloatForKey("separation") ?? PaddingDefault - MVMCoreUIStackableViewController.populateView(self, withUIArray: moleculesArray, useMargins: useMargins) { (object) -> UIEdgeInsets in - return UIEdgeInsets.init(top: separation, left: 0, bottom: 0, right: 0) + alignHorizontal(ViewConstrainingView.getAlignmentFor(json?.optionalStringForKey("alignment"), defaultAlignment: .fill)) + alignVertical(.leading) + } + + // Adds the molecules and sets the json. + for (index, map) in molecules.enumerated() { + 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) + view = item.view + 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) } } } + + 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 + } + for case let item as [AnyHashable: AnyHashable] in molecules { + if let moleculeName = item.stringOptionalWithChainOfKeysOrIndexes([KeyMolecule, KeyMoleculeName]) { + name.append(moleculeName) + } + } + return name + } + + // MARK: - Adding to stack + /// Adds the view to the stack. + func addView(_ view: UIView, lastItem: Bool) { + addStackItem(StackItem(with: view), lastItem: lastItem) + } + + /// Adds the stack item to the stack. + func addStackItem(_ stackItem: StackItem, lastItem: Bool) { + let view = stackItem.view + contentView.addSubview(view) + view.translatesAutoresizingMaskIntoConstraints = false + + let spacing = stackItem.spacing ?? self.spacing + if let view = view as? MVMCoreUIViewConstrainingProtocol { + let verticalAlignment = stackItem.verticalAlignment ?? (stackItem.percentage == nil && axis == .vertical ? .fill : (axis == .vertical ? .leading : .center)) + let horizontalAlignment = stackItem.horizontalAlignment ?? view.alignment?() ?? (axis == .vertical || stackItem.percentage == nil ? .fill : .leading) + view.alignHorizontal?(horizontalAlignment) + view.alignVertical?(verticalAlignment) + } + if axis == .vertical { + if items.count == 0 { + pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: spacing) + } else if let previousView = items.last?.view { + _ = NSLayoutConstraint(pinFirstView: previousView, toSecondView: view, withConstant: spacing, directionVertical: true) + } + pinView(view, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: 0) + pinView(contentView, toView: view, attribute: .trailing, relation: .equal, priority: .required, constant: 0) + if let percent = stackItem.percentage { + view.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: CGFloat(percent)/100.0).isActive = true + } + if lastItem { + pinView(contentView, toView: view, attribute: .bottom, relation: .equal, priority: .required, constant: 0) + } + } 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) + } else if let previousView = items.last?.view { + _ = NSLayoutConstraint(pinFirstView: previousView, toSecondView: view, withConstant: spacing, directionVertical: false) + } + pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: 0) + pinView(contentView, toView: view, attribute: .bottom, relation: .equal, priority: .required, constant: 0) + if let percent = stackItem.percentage { + view.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: CGFloat(percent)/100.0).isActive = true + } + } + if lastItem { + pinView(contentView, toView: view, attribute: .right, relation: .equal, priority: .required, constant: 0) + } + items.append(stackItem) + } } diff --git a/MVMCoreUI/Molecules/MoleculeTableViewCell.swift b/MVMCoreUI/Molecules/MoleculeTableViewCell.swift index 298fa6b5..695b5849 100644 --- a/MVMCoreUI/Molecules/MoleculeTableViewCell.swift +++ b/MVMCoreUI/Molecules/MoleculeTableViewCell.swift @@ -43,11 +43,25 @@ import UIKit public func updateView(_ size: CGFloat) { MFStyler.setDefaultMarginsFor(self, size: size, horizontal: true, vertical: true) if #available(iOS 11.0, *) { - contentView.directionalLayoutMargins = directionalLayoutMargins + if accessoryView != nil { + // Smaller left margin if accessory view. + var margin = directionalLayoutMargins + margin.trailing = 16 + contentView.directionalLayoutMargins = margin + } else { + contentView.directionalLayoutMargins = directionalLayoutMargins + } topSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading) bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading) } else { - contentView.layoutMargins = layoutMargins + if accessoryView != nil { + // Smaller left margin if accessory view. + var margin = layoutMargins + margin.right = 16 + contentView.layoutMargins = margin + } else { + contentView.layoutMargins = layoutMargins + } topSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left) bottomSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left) } @@ -67,7 +81,7 @@ import UIKit } // MARK: - MVMCoreUIMoleculeViewProtocol - public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { self.json = json; guard let json = json, let moleculeJSON = json.optionalDictionaryForKey(KeyMolecule) else { return @@ -75,15 +89,17 @@ import UIKit 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 + var standardConstraints = true + if let castView = moleculeView as? MVMCoreUIViewConstrainingProtocol { + standardConstraints = castView.useStandardConstraints?() ?? true + castView.shouldSetHorizontalMargins?(!standardConstraints) + castView.shouldSetVerticalMargins?(!standardConstraints) + } NSLayoutConstraint.activate(Array(NSLayoutConstraint.pinView(toSuperview: moleculeView, useMargins: standardConstraints).values)) if standardConstraints { let constraint = contentView.heightAnchor.constraint(equalToConstant: 80) constraint.priority = .defaultLow constraint.isActive = true - if let moleculeView = moleculeView as? ViewConstrainingView { - moleculeView.updateViewHorizontalDefaults = false - } } molecule = moleculeView } @@ -110,15 +126,27 @@ import UIKit molecule?.reset?() } - public static func estimatedHeight(forRow json: [AnyHashable : Any]?) -> CGFloat { + public static func estimatedHeight(forRow json: [AnyHashable: Any]?) -> CGFloat { guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule), let theClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(withJSON: moleculeJSON, delegateObject: nil), let estimatedHeightFor = theClass.estimatedHeight else { - return 0 + return 80 } return estimatedHeightFor(moleculeJSON) } + public static func name(forReuse molecule: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { + if let molecule = molecule?.optionalDictionaryForKey(KeyMolecule), + let moleculeName = molecule.optionalStringForKey(KeyMoleculeName), + let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping[moleculeName] as? AnyClass, + let castClass = moleculeClass as? MVMCoreUIMoleculeViewProtocol.Type, + let nameFunc = castClass.name { + return nameFunc(molecule, delegateObject) + } else { + return molecule?.optionalDictionaryForKey(KeyMolecule)?.optionalStringForKey(KeyMoleculeName) + } + } + // MARK: - Arrow /// Adds the standard mvm style caret to the accessory view public func addCaretViewAccessory() { @@ -129,16 +157,12 @@ import UIKit let height: CGFloat = 10 caretView = CaretView(lineThickness: CaretView.thin) caretView?.frame = CGRect(x: 0, y: 0, width: width, height: height) - caretViewWidthSizeObject = MFSizeObject(scalingStandardSize: width) - caretViewHeightSizeObject = MFSizeObject(scalingStandardSize: height) + caretViewWidthSizeObject = MFSizeObject(standardSize: width, standardiPadPortraitSize: 9) + caretViewHeightSizeObject = MFSizeObject(standardSize: height, standardiPadPortraitSize: 16) accessoryView = caretView } // MARK: - MoleculeListCellProtocol - public static func moleculeName(_ molecule: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> String? { - return molecule?.optionalDictionaryForKey(KeyMolecule)?.optionalStringForKey(KeyMoleculeName) - } - /// 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() @@ -155,7 +179,7 @@ import UIKit } } - public func didSelectCell(atIndex indexPath: IndexPath, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + 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) } diff --git a/MVMCoreUI/Molecules/StandardHeaderView.swift b/MVMCoreUI/Molecules/StandardHeaderView.swift index 26ce345b..2391f062 100644 --- a/MVMCoreUI/Molecules/StandardHeaderView.swift +++ b/MVMCoreUI/Molecules/StandardHeaderView.swift @@ -68,7 +68,6 @@ public class StandardHeaderView: ViewConstrainingView { if let separatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionBot, withHorizontalPadding: 0) { separatorView.setAsHeavy() - separatorView.isHidden = true addSubview(separatorView) self.separatorView = separatorView } diff --git a/MVMCoreUI/Molecules/TwoButtonView.swift b/MVMCoreUI/Molecules/TwoButtonView.swift index 48becca2..c5085cce 100644 --- a/MVMCoreUI/Molecules/TwoButtonView.swift +++ b/MVMCoreUI/Molecules/TwoButtonView.swift @@ -103,8 +103,8 @@ import UIKit addSubview(viewForButtons) self.viewForButtons = viewForButtons - setupConstraints(forView: viewForButtons) - alignCenter() + pinView(toSuperView: viewForButtons) + alignCenterHorizontal() createPrimaryButton() createSecondaryButton() @@ -118,8 +118,8 @@ import UIKit createPrimaryButton() if let primaryButton = primaryButton { addSubview(primaryButton) - setupConstraints(forView: primaryButton) - alignCenter() + pinView(toSuperView: primaryButton) + alignCenterHorizontal() } } diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIViewConstrainingProtocol.h b/MVMCoreUI/OtherHandlers/MVMCoreUIViewConstrainingProtocol.h index 8bdc3eb1..42114416 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIViewConstrainingProtocol.h +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIViewConstrainingProtocol.h @@ -21,4 +21,14 @@ /// Can be used to override any standard constraints that may be added. - (BOOL)useStandardConstraints; +/// Will align if it can. +- (void)alignHorizontal:(UIStackViewAlignment)alignment; +- (void)alignVertical:(UIStackViewAlignment)alignment; + +/// Containing Views can tell the contained if they should use horizontal margins. +- (void)shouldSetHorizontalMargins:(BOOL)shouldSet; + +/// Containing Views can tell the contained if they should use vertical margins. +- (void)shouldSetVerticalMargins:(BOOL)shouldSet; + @end diff --git a/MVMCoreUI/Styles/MFStyler.h b/MVMCoreUI/Styles/MFStyler.h index b8c80752..bda6c112 100644 --- a/MVMCoreUI/Styles/MFStyler.h +++ b/MVMCoreUI/Styles/MFStyler.h @@ -205,6 +205,9 @@ B3 -> Legal #pragma mark - 2.0 styles +/// Will style the label based on the string. Accepted values, H1, H2, H3, H32, B1, B2, B3, B20 ++ (void)styleLabel:(nonnull UILabel *)label withStyle:(nullable NSString *)style; + + (void)styleLabelH1:(nonnull UILabel *)label genericScaling:(BOOL)genericScaling; + (void)styleLabelH1:(nonnull UILabel *)label; @@ -250,6 +253,9 @@ B3 -> Legal #pragma mark - Attributed Strings +/// Will style the string based on the string. Accepted values, H1, H2, H3, H32, B1, B2, B3, B20 ++ (nonnull NSAttributedString *)styleGetAttributedString:(nullable NSString *)string withStyle:(nullable NSString *)style; + + (nonnull NSAttributedString *)styleGetAttributedString:(nullable NSString *)string font:(nonnull UIFont *)font color:(nonnull UIColor *)color; + (nonnull NSAttributedString *)styleGetH1AttributedString:(nullable NSString *)string; + (nonnull NSAttributedString *)styleGetH2AttributedString:(nullable NSString *)string; diff --git a/MVMCoreUI/Styles/MFStyler.m b/MVMCoreUI/Styles/MFStyler.m index 4f27b2be..dc9288d8 100644 --- a/MVMCoreUI/Styles/MFStyler.m +++ b/MVMCoreUI/Styles/MFStyler.m @@ -511,6 +511,26 @@ CGFloat const LabelWithInternalButtonLineSpace = 2; #pragma mark - 2.0 Styles ++ (void)styleLabel:(nonnull UILabel *)label withStyle:(nullable NSString *)style { + if ([style isEqualToString:@"H1"]) { + [self styleLabelH1:label]; + } else if ([style isEqualToString:@"H2"]) { + [self styleLabelH2:label]; + } else if ([style isEqualToString:@"H3"]) { + [self styleLabelH3:label]; + } else if ([style isEqualToString:@"H32"]) { + [self styleLabelH32:label]; + } else if ([style isEqualToString:@"B1"]) { + [self styleLabelB1:label]; + } else if ([style isEqualToString:@"B3"]) { + [self styleLabelB3:label]; + } else if ([style isEqualToString:@"B20"]) { + [self styleLabelB20:label]; + } else { + [self styleLabelB2:label]; + } +} + + (void)styleLabelH1:(nonnull UILabel *)label genericScaling:(BOOL)genericScaling { label.font = [MFStyler fontH1:genericScaling]; label.textColor = [UIColor blackColor]; @@ -630,6 +650,26 @@ CGFloat const LabelWithInternalButtonLineSpace = 2; #pragma mark - Attributed Strings ++ (nonnull NSAttributedString *)styleGetAttributedString:(nullable NSString *)string withStyle:(nullable NSString *)style { + if ([style isEqualToString:@"H1"]) { + return [self styleGetH1AttributedString:string]; + } else if ([style isEqualToString:@"H2"]) { + return [self styleGetH2AttributedString:string]; + } else if ([style isEqualToString:@"H3"]) { + return [self styleGetH3AttributedString:string]; + } else if ([style isEqualToString:@"H32"]) { + return [self styleGetH32AttributedString:string]; + } else if ([style isEqualToString:@"B1"]) { + return [self styleGetB1AttributedString:string]; + } else if ([style isEqualToString:@"B3"]) { + return [self styleGetB3AttributedString:string]; + } else if ([style isEqualToString:@"B20"]) { + return [self styleGetB20AttributedString:string]; + } else { + return [self styleGetB2AttributedString:string]; + } +} + + (nonnull NSAttributedString *)styleGetAttributedString:(nullable NSString *)string font:(nonnull UIFont *)font color:(nonnull UIColor *)color { NSAttributedString *attributedString = nil; if (![string isEqual:[NSNull null]] && string.length > 0) { @@ -656,6 +696,10 @@ CGFloat const LabelWithInternalButtonLineSpace = 2; return [MFStyler styleGetAttributedString:string font:[MFStyler fontH3] color:[UIColor blackColor]]; } ++ (nonnull NSAttributedString *)styleGetH32AttributedString:(nullable NSString *)string { + return [MFStyler styleGetAttributedString:string font:[MFStyler fontH32] color:[UIColor blackColor]]; +} + + (nonnull NSAttributedString *)styleGetB1AttributedString:(nullable NSString *)string { return [MFStyler styleGetAttributedString:string font:[MFStyler fontB1] color:[UIColor blackColor]]; } @@ -668,6 +712,10 @@ CGFloat const LabelWithInternalButtonLineSpace = 2; return [MFStyler styleGetAttributedString:string font:[MFStyler fontB3] color:[UIColor mfBattleshipGrey]]; } ++ (nonnull NSAttributedString *)styleGetB20AttributedString:(nullable NSString *)string { + return [MFStyler styleGetAttributedString:string font:[MFStyler fontB20] color:[UIColor blackColor]]; +} + + (nonnull NSAttributedString *)styleGetDisabledB1AttributedString:(nullable NSString *)string { return [MFStyler styleGetAttributedString:string font:[MFStyler fontB1] color:[UIColor mfLighterGrayColor]]; } diff --git a/MVMCoreUI/Styles/usage.json b/MVMCoreUI/Styles/usage.json new file mode 100644 index 00000000..0f6ee862 --- /dev/null +++ b/MVMCoreUI/Styles/usage.json @@ -0,0 +1,108 @@ +{ +"pageType": "usage", +"template": "moleculeList", +"header": { + "moleculeName":"header", + "headline":{ + "moleculeName": "label", + "text":"See who's using what." +}, +"body":{ + "moleculeName": "label", + "text":"Data usage was estimated as of Jun 11." +}, +"molecules": [{ + "moleculeName": "listItem", + "molecule" : { + "moleculeName":"moleculeStack", + "axis": "horizontal", + "molecules": [{ + "percent": 60, + "molecule": { + "moleculeName": "label", + "text": "Johny Verizon\n555.555.5555\nGo Unlimited", + "attributes": [{ + "location": 0, + "length": 13, + "type": "font", + "style": "B1" + }] + } + },{ + "horizontalAlignment": "trailing", + "molecule": { + "moleculeName": "label", + "text": "4.55 GB" + } + }] + }, + "actionMap":{ + "actionType": "openPage", + "pageType": "usageDetails", + "extraParameters": { + "mdn": "5555555555" + } + }},{ + "moleculeName": "listItem", + "molecule" : { + "moleculeName":"moleculeStack", + "axis": "horizontal", + "molecules": [{ + "percent": 60, + "molecule": { + "moleculeName": "label", + "text": "Sree Verizon\n555.555.5556", + "attributes": [{ + "location": 0, + "length": 12, + "type": "font", + "style": "B1" + }] + } + },{ + "horizontalAlignment": "trailing", + "molecule": { + "moleculeName": "label", + "text": "0 GB" + } + }] + }, + "actionMap":{ + "actionType": "openPage", + "pageType": "usageDetails", + "extraParameters": { + "mdn": "5555555556" + } + }},{ + "moleculeName": "listItem", + "molecule" : { + "moleculeName":"moleculeStack", + "axis": "horizontal", + "molecules": [{ + "percent": 60, + "molecule": { + "moleculeName": "label", + "text": "Vishal Verizon\n555.555.5557\nGo Unlimited", + "attributes": [{ + "location": 0, + "length": 14, + "type": "font", + "style": "B1" + }] + } + },{ + "horizontalAlignment": "trailing", + "molecule": { + "moleculeName": "label", + "text": "0 GB" + } + }] + }, + "actionMap":{ + "actionType": "openPage", + "pageType": "usageDetails", + "extraParameters": { + "mdn": "5555555557" + } + }}] +} \ No newline at end of file diff --git a/MVMCoreUI/Templates/MoleculeListCellProtocol.h b/MVMCoreUI/Templates/MoleculeListCellProtocol.h index 08ca2a37..7603c820 100644 --- a/MVMCoreUI/Templates/MoleculeListCellProtocol.h +++ b/MVMCoreUI/Templates/MoleculeListCellProtocol.h @@ -10,8 +10,6 @@ @protocol MoleculeListCellProtocol @optional -/// Can override the molecule name for the given molecule. Otherwise we assume value for key moleculeName. -+ (nullable NSString *)moleculeName:(nullable NSDictionary *)molecule delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject; /// Can set the separator according to what the moleculeList commands. - (void)setSeparatorWithJSON:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData indexPath:(nonnull NSIndexPath *)indexPath; diff --git a/MVMCoreUI/Templates/MoleculeListTemplate.swift b/MVMCoreUI/Templates/MoleculeListTemplate.swift index 9f8b7707..ea3f94b4 100644 --- a/MVMCoreUI/Templates/MoleculeListTemplate.swift +++ b/MVMCoreUI/Templates/MoleculeListTemplate.swift @@ -36,7 +36,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { override open func viewForBottom() -> UIView { guard let moleculeJSON = loadObject?.pageJSON?.optionalDictionaryForKey("footer"), let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject() as? MVMCoreUIDelegateObject, constrainIfNeeded: true) else { - return viewForBottom() + return super.viewForBottom() } return molecule } @@ -117,7 +117,7 @@ open class MoleculeListTemplate: ThreeLayerTableViewController { guard let map = molecule.molecule, let moleculeName = map.optionalStringForKey(KeyMoleculeName), let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping[moleculeName] as? AnyClass else { return nil } - if let moleculeClass = moleculeClass as? MoleculeListCellProtocol.Type, let moleculeNameFunc = moleculeClass.moleculeName { + if let moleculeClass = moleculeClass as? MVMCoreUIMoleculeViewProtocol.Type, let moleculeNameFunc = moleculeClass.name { return (moleculeNameFunc(map, delegateObject() as? MVMCoreUIDelegateObject), moleculeClass) } else { return (moleculeName, moleculeClass)