diff --git a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m index a88b61c3..780b65aa 100644 --- a/MVMCoreUI/Atoms/Views/ViewConstrainingView.m +++ b/MVMCoreUI/Atoms/Views/ViewConstrainingView.m @@ -138,7 +138,6 @@ #pragma mark - MVMCoreUIMoleculeViewProtocol - (void)setAsMolecule { - self.updateViewHorizontalDefaults = YES; } @end diff --git a/MVMCoreUI/BaseControllers/MVMCoreUIStackableViewController.h b/MVMCoreUI/BaseControllers/MVMCoreUIStackableViewController.h index 9e7d352e..0c702925 100644 --- a/MVMCoreUI/BaseControllers/MVMCoreUIStackableViewController.h +++ b/MVMCoreUI/BaseControllers/MVMCoreUIStackableViewController.h @@ -29,13 +29,15 @@ // Returns the standard space around ui objects. + (UIEdgeInsets)standardSpaceAroundUIObjectForSize:(CGFloat)size; -// Consolidates generateFormViewWithUIArray and spaceArroundUIObject into one class object so other view controllers can take advantage of these functions. +// Consolidates generateFormViewWithUIArray and spaceArroundUIObject into one class object so other view controllers can take advantage of these functions. By default does not pin to margins. + (void)populateView:(nonnull UIView *)view withUIArray:(nonnull NSArray *)formUIArray withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock; ++ (void)populateView:(nonnull UIView *)view withUIArray:(nonnull NSArray *)formUIArray useMargins:(BOOL)useMargins withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock; + (void)populateViewHorizontally:(nonnull UIView *)view withUIArray:(nonnull NSArray *)formUIArray withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock; + (void)populateView:(nonnull UIView *)view withUIArrayForConstrainingViews:(nonnull NSArray *)formUIArray withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock; -// Does the actual laying out. The formuiarray views should already be added to the view. +// Does the actual laying out. The formuiarray views should already be added to the view. By default does not pin to margins + (void)autoLayoutView:(nonnull UIView *)view withUIArray:(nonnull NSArray *)formUIArray withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock; ++ (void)autoLayoutView:(nonnull UIView *)view withUIArray:(nonnull NSArray *)formUIArray useMargins:(BOOL)useMargins withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock; + (void)autoLayoutViewHorizontally:(nonnull UIView *)view withUIArray:(nonnull NSArray *)formUIArray withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock; + (void)autoLayoutViewWithConstrainingViewsWithUIArray:(nonnull NSArray *)formUIArray withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock; diff --git a/MVMCoreUI/BaseControllers/MVMCoreUIStackableViewController.m b/MVMCoreUI/BaseControllers/MVMCoreUIStackableViewController.m index 86f4ebfe..7a14762a 100644 --- a/MVMCoreUI/BaseControllers/MVMCoreUIStackableViewController.m +++ b/MVMCoreUI/BaseControllers/MVMCoreUIStackableViewController.m @@ -10,6 +10,7 @@ #import "MFStyler.h" #import "ViewConstrainingView.h" #import "MVMCoreUIUtility.h" +#import "NSLayoutConstraint+MFConvenience.h" @interface MVMCoreUIStackableViewController () @end @@ -69,11 +70,15 @@ } + (void)populateView:(nonnull UIView *)view withUIArray:(nonnull NSArray *)formUIArray withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock { + [self populateView:view withUIArray:formUIArray useMargins:NO withSpacingBlock:spacingBlock]; +} + ++ (void)populateView:(nonnull UIView *)view withUIArray:(nonnull NSArray *)formUIArray useMargins:(BOOL)useMargins withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock { if ([formUIArray count] > 0) { for (UIView *subview in formUIArray) { [view addSubview:subview]; } - [MVMCoreUIStackableViewController autoLayoutView:view withUIArray:formUIArray withSpacingBlock:spacingBlock]; + [MVMCoreUIStackableViewController autoLayoutView:view withUIArray:formUIArray useMargins:useMargins withSpacingBlock:spacingBlock]; } } @@ -96,34 +101,46 @@ } } -+ (void)autoLayoutView:(nonnull UIView *)view withUIArray:(nonnull NSArray *)formUIArray withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock { ++ (void)autoLayoutView:(nonnull UIView *)view withUIArray:(nonnull NSArray *)formUIArray useMargins:(BOOL)useMargins withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock { if ([formUIArray count] > 0) { // Adds the first object to the view and pins it to the top of the content view. - id previousUIObject = [formUIArray objectAtIndex:0]; - UIEdgeInsets spaceAroundObjectPrevious = spacingBlock(previousUIObject); + UIView *previousUIObject = [formUIArray objectAtIndex:0]; [previousUIObject setTranslatesAutoresizingMaskIntoConstraints:NO]; - [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-top@999-[previousUIObject]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:@{@"top":@(spaceAroundObjectPrevious.top)} views:NSDictionaryOfVariableBindings(previousUIObject)]]; - [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-left@999-[previousUIObject]-right-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:@{@"left":@(spaceAroundObjectPrevious.left),@"right":@(spaceAroundObjectPrevious.right)} views:NSDictionaryOfVariableBindings(previousUIObject)]]; + UIEdgeInsets spaceAroundObjectPrevious = spacingBlock(previousUIObject); + NSLayoutConstraint *constraint = [NSLayoutConstraint pinViewTopToSuperview:previousUIObject useMargins:useMargins constant:spaceAroundObjectPrevious.top]; + constraint.priority = 999; + constraint.active = YES; + constraint = [NSLayoutConstraint pinViewLeftToSuperview:previousUIObject useMargins:useMargins constant:spaceAroundObjectPrevious.left]; + constraint.priority = 999; + constraint.active = YES; + [NSLayoutConstraint pinViewRightToSuperview:previousUIObject useMargins:useMargins constant:spaceAroundObjectPrevious.right].active = YES; // Sets the horizontal spacing and adds vertical spacing between all ui objects. for (NSUInteger i = 1; i < [formUIArray count]; i++) { - id uiObject = [formUIArray objectAtIndex:i]; + UIView *uiObject = [formUIArray objectAtIndex:i]; UIEdgeInsets spaceAroundObjectCurrent = spacingBlock(uiObject); [uiObject setTranslatesAutoresizingMaskIntoConstraints:NO]; [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[previousUIObject]-space-[uiObject]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:@{@"space":@(spaceAroundObjectPrevious.bottom + spaceAroundObjectCurrent.top)} views:NSDictionaryOfVariableBindings(previousUIObject,uiObject)]]; - [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-left@999-[uiObject]-right-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:@{@"left":@(spaceAroundObjectCurrent.left),@"right":@(spaceAroundObjectCurrent.right)} views:NSDictionaryOfVariableBindings(uiObject)]]; - + constraint = [NSLayoutConstraint pinViewLeftToSuperview:uiObject useMargins:useMargins constant:spaceAroundObjectCurrent.left]; + constraint.priority = 999; + constraint.active = YES; + [NSLayoutConstraint pinViewRightToSuperview:uiObject useMargins:useMargins constant:spaceAroundObjectCurrent.right].active = YES; + previousUIObject = uiObject; spaceAroundObjectPrevious = spaceAroundObjectCurrent; } // Pins the last object to the bottom of the content view. - [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[previousUIObject]-bottom-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:@{@"bottom":@(spaceAroundObjectPrevious.bottom)} views:NSDictionaryOfVariableBindings(previousUIObject)]]; + [NSLayoutConstraint pinViewBottomToSuperview:previousUIObject useMargins:useMargins constant:spaceAroundObjectPrevious.bottom].active = YES; } } ++ (void)autoLayoutView:(nonnull UIView *)view withUIArray:(nonnull NSArray *)formUIArray withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock { + [self autoLayoutView:view withUIArray:formUIArray useMargins:NO withSpacingBlock:spacingBlock]; +} + + (void)autoLayoutViewHorizontally:(nonnull UIView *)view withUIArray:(nonnull NSArray *)formUIArray withSpacingBlock:(nonnull UIEdgeInsets (^) (id _Nullable object))spacingBlock { if ([formUIArray count] > 0) { diff --git a/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift b/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift index c1fadf55..2573388c 100644 --- a/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift +++ b/MVMCoreUI/BaseControllers/ThreeLayerViewController.swift @@ -16,6 +16,7 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController { var topView: UIView? var middleView: UIView? var bottomView: UIView? + var useMargins: Bool = true // The bottom view can be put outside of the scrolling area. var bottomViewOutsideOfScroll = false @@ -26,6 +27,7 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController { open override func updateViews() { super.updateViews() let width = view.bounds.width + MFStyler.setDefaultMarginsFor(contentView, size: width) if let topView = topView as? MVMCoreViewProtocol { topView.updateView(width) } @@ -116,9 +118,9 @@ extension ThreeLayerViewController { return nil } contentView.addSubview(topView) - topView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true - contentView.rightAnchor.constraint(equalTo: topView.rightAnchor).isActive = true - topView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true + NSLayoutConstraint.pinViewTop(toSuperview: topView, useMargins: useMargins, constant: 0).isActive = true + NSLayoutConstraint.pinViewLeft(toSuperview: topView, useMargins: useMargins, constant: 0).isActive = true + NSLayoutConstraint.pinViewRight(toSuperview: topView, useMargins: useMargins, constant: 0).isActive = true return topView } @@ -133,8 +135,8 @@ extension ThreeLayerViewController { return nil } contentView.addSubview(middleView) - middleView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true - contentView.rightAnchor.constraint(equalTo: middleView.rightAnchor).isActive = true + NSLayoutConstraint.pinViewLeft(toSuperview: middleView, useMargins: useMargins, constant: 0).isActive = true + NSLayoutConstraint.pinViewRight(toSuperview: middleView, useMargins: useMargins, constant: 0).isActive = true middleView.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical) return middleView } @@ -220,17 +222,17 @@ extension ThreeLayerViewController { return } contentView.addSubview(view); - contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true - contentView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true - view.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true + NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true + NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true + NSLayoutConstraint.pinViewBottom(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true } func addViewOutsideOfScrollViewBottom(_ view: UIView) { self.view?.addSubview(view) if let scrollView = scrollView, let parentView = self.view { view.topAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true - view.leftAnchor.constraint(equalTo: parentView.leftAnchor).isActive = true - parentView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true + NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true + NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true if #available(iOS 11.0, *) { parentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true if let safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: parentView) { @@ -238,7 +240,7 @@ extension ThreeLayerViewController { self.safeAreaView = safeAreaView } } else { - parentView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + NSLayoutConstraint.pinViewBottom(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true } } } diff --git a/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.h b/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.h index ed9d1876..c8e12cae 100644 --- a/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.h +++ b/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.h @@ -55,6 +55,13 @@ extern NSString * _Nonnull const ConstraintWidth; +(nullable NSDictionary *)constraintAlignView :(nonnull UIView *)firstView toSecondView :(nonnull UIView *)secondView alignX :(BOOL)alignX alignY :(BOOL)alignY; +#pragma mark - With Margins + ++ (nonnull NSLayoutConstraint *)pinViewTopToSuperview:(nonnull UIView *)view useMargins:(BOOL)useMargins constant:(CGFloat)constant; ++ (nonnull NSLayoutConstraint *)pinViewLeftToSuperview:(nonnull UIView *)view useMargins:(BOOL)useMargins constant:(CGFloat)constant; ++ (nonnull NSLayoutConstraint *)pinViewRightToSuperview:(nonnull UIView *)view useMargins:(BOOL)useMargins constant:(CGFloat)constant; ++ (nonnull NSLayoutConstraint *)pinViewBottomToSuperview:(nonnull UIView *)view useMargins:(BOOL)useMargins constant:(CGFloat)constant; + #pragma mark - Scaling constraints // These functions will pin a view to its super view with space that is a ratio of the anchor (by default the super view's width if nothing is passed in). (So if the space is 24 and the super's width is 320 in the design, then the ratio should be 24/320 and this will be multiplied by the super's current width, so that the constraint always linearly scales). diff --git a/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.m b/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.m index 1ed0acd1..39a315c1 100644 --- a/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.m +++ b/MVMCoreUI/Categories/NSLayoutConstraint+MFConvenience.m @@ -150,6 +150,24 @@ NSString *const ConstraintWidth = @"width"; return constraintsDictionary; } +#pragma mark - With Margins + ++ (NSLayoutConstraint *)pinViewTopToSuperview:(UIView *)view useMargins:(BOOL)useMargins constant:(CGFloat)constant { + return [view.topAnchor constraintEqualToAnchor:(useMargins ? view.superview.layoutMarginsGuide.topAnchor : view.superview.topAnchor) constant:constant]; +} + ++ (NSLayoutConstraint *)pinViewLeftToSuperview:(UIView *)view useMargins:(BOOL)useMargins constant:(CGFloat)constant { + return [view.leftAnchor constraintEqualToAnchor:(useMargins ? view.superview.layoutMarginsGuide.leftAnchor : view.superview.leftAnchor) constant:constant]; +} + ++ (NSLayoutConstraint *)pinViewRightToSuperview:(UIView *)view useMargins:(BOOL)useMargins constant:(CGFloat)constant { + return [(useMargins ? view.superview.layoutMarginsGuide.rightAnchor : view.superview.rightAnchor) constraintEqualToAnchor:view.rightAnchor constant:constant]; +} + ++ (NSLayoutConstraint *)pinViewBottomToSuperview:(UIView *)view useMargins:(BOOL)useMargins constant:(CGFloat)constant { + return [(useMargins ? view.superview.layoutMarginsGuide.bottomAnchor : view.superview.bottomAnchor) constraintEqualToAnchor:view.bottomAnchor constant:constant]; +} + #pragma mark - Scaling constraints + (nullable NSDictionary *)scalingConstraintPinSubview:(nullable UIView *)subview pinTop:(BOOL)pinTop topConstant:(CGFloat)topConstant pinBottom:(BOOL)pinBottom bottomConstant:(CGFloat)bottomConstant pinLeft:(BOOL)pinLeft leftConstant:(CGFloat)leftConstant pinRight:(BOOL)pinRight rightConstant:(CGFloat)rightConstant baseConstant:(CGFloat)baseConstant { diff --git a/MVMCoreUI/Molecules/MoleculeStackView.swift b/MVMCoreUI/Molecules/MoleculeStackView.swift index ab27c481..1c70d6eb 100644 --- a/MVMCoreUI/Molecules/MoleculeStackView.swift +++ b/MVMCoreUI/Molecules/MoleculeStackView.swift @@ -11,7 +11,8 @@ import UIKit public class MoleculeStackView: MFView { var spacingBlock: ((Any) -> UIEdgeInsets)? var moleculesArray: [UIView]? - + var useMargins: Bool = false + public override init(frame: CGRect) { super.init(frame: frame) } @@ -67,9 +68,9 @@ public class MoleculeStackView: MFView { } if let spacingBlock = spacingBlock { - MVMCoreUIStackableViewController.populateView(self, withUIArray: moleculesArray, withSpacingBlock: spacingBlock) + MVMCoreUIStackableViewController.populateView(self, withUIArray: moleculesArray, useMargins: useMargins, withSpacingBlock: spacingBlock) } else { - MVMCoreUIStackableViewController.populateView(self, withUIArray: moleculesArray) { (object) -> UIEdgeInsets in + MVMCoreUIStackableViewController.populateView(self, withUIArray: moleculesArray, useMargins: useMargins) { (object) -> UIEdgeInsets in if object as AnyObject? === moleculesArray.first { return UIEdgeInsets.zero } else { diff --git a/MVMCoreUI/Molecules/TwoButtonView.swift b/MVMCoreUI/Molecules/TwoButtonView.swift index aaa1f500..c56d82bf 100644 --- a/MVMCoreUI/Molecules/TwoButtonView.swift +++ b/MVMCoreUI/Molecules/TwoButtonView.swift @@ -25,6 +25,11 @@ import UIKit super.init(frame: frame) } + public func setDefaultCustom() { + primaryButton?.setAsStandardCustom() + secondaryButton?.setAsSecondaryCustom() + } + // MARK: - MVMCoreViewProtocol open override func updateView(_ size: CGFloat) { super.updateView(size) @@ -139,8 +144,7 @@ import UIKit open func set(primaryButtonJSON: [AnyHashable: Any]?, secondaryButtonJSON: [AnyHashable: Any]?, actionDelegate: NSObjectProtocol?, additionalData: [AnyHashable: Any]?, buttonDelegate: Any?) { setupUI(withPrimaryButtonMap: primaryButtonJSON, secondaryButtonMap: secondaryButtonJSON, legacy: false) - primaryButton?.setAsStandardCustom() - secondaryButton?.setAsSecondaryCustom() + setDefaultCustom() primaryButton?.setWithJSON(primaryButtonJSON, delegate: actionDelegate as? NSObject, additionalData: additionalData) secondaryButton?.setWithJSON(secondaryButtonJSON, delegate: actionDelegate as? NSObject, additionalData: additionalData) } diff --git a/MVMCoreUI/Styles/MFStyler.h b/MVMCoreUI/Styles/MFStyler.h index 067cd120..49f02db0 100644 --- a/MVMCoreUI/Styles/MFStyler.h +++ b/MVMCoreUI/Styles/MFStyler.h @@ -90,6 +90,7 @@ B3 -> Legal + (CGFloat)defaultVerticalPaddingForApplicationWidth; + (CGFloat)defaultHorizontalPaddingForSize:(CGFloat)size; + (CGFloat)defaultVerticalPaddingForSize:(CGFloat)size; ++ (void)setDefaultMarginsForView:(nullable UIView *)view size:(CGFloat)size; //------------------------------------------------- // Returns the fonts for these styles. Scales them as needed by default diff --git a/MVMCoreUI/Styles/MFStyler.m b/MVMCoreUI/Styles/MFStyler.m index 36d04e51..e7f14546 100644 --- a/MVMCoreUI/Styles/MFStyler.m +++ b/MVMCoreUI/Styles/MFStyler.m @@ -12,6 +12,7 @@ #import "UIColor+MFConvenience.h" #import "NSLayoutConstraint+MFConvenience.h" #import "MVMCoreUISplitViewController.h" +@import MVMCore.MVMCoreDispatchUtility; CGFloat const PaddingDefault = 24; CGFloat const PaddingDefaultHorizontalSpacing = 32; @@ -88,6 +89,17 @@ CGFloat const LabelWithInternalButtonLineSpace = 2; return [[MFSizeObject sizeObjectWithScalingStandardSize:PaddingDefaultVerticalSpacing] getValueBasedOnSize:size]; } ++ (void)setDefaultMarginsForView:(nullable UIView *)view size:(CGFloat)size { + [MVMCoreDispatchUtility performBlockOnMainThread:^{ + CGFloat padding = [MFStyler defaultHorizontalPaddingForSize:size]; + if (@available(iOS 11.0, *)) { + view.directionalLayoutMargins = NSDirectionalEdgeInsetsMake(0, padding, 0, padding); + } else { + view.layoutMargins = UIEdgeInsetsMake(0, padding, 0, padding); + } + }]; +} + #pragma mark - 2.0 fonts + (nullable UIFont *)fontH1:(BOOL)genericScaling {