diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 3915b055..ec7e68b0 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -2103,7 +2103,7 @@ 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */, 011B58F023A2AA980085F53C /* ListItemModelProtocol.swift in Sources */, D22479962316AF6E003FCCF9 /* HeadlineBodyLink.swift in Sources */, - 8DE5BECD2456F7A200772E76 /* ListTwoColumnDropdownSelectorsModel.swift in Sources */, + 8DE5BECD2456F7A200772E76 /* ListTwoColumnDropdownSelectorsModel.swift in Sources */, 0A41BA7F23453A6400D4C0BC /* TextEntryField.swift in Sources */, BB55B51D244482C1002001AD /* ListRightVariablePriceChangeBodyText.swift in Sources */, 017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift index abdc1242..601a8fb2 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift @@ -8,40 +8,75 @@ import UIKit -public enum ButtonStyle: String, Codable { - case primary - case secondary -} +public typealias FacadeElements = (fill: UIColor?, text: UIColor?, border: UIColor?) -public enum ButtonSize: String, Codable { - case standard - case tiny -} public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "button" public var backgroundColor: Color? public var title: String public var action: ActionModelProtocol public var enabled: Bool = true - public var style: ButtonStyle? - public var size: ButtonSize? = .standard - public var fillColor: Color? - public var textColor: Color? - public var borderColor: Color? + public var style: Styler.Button.Style? { + didSet { + guard let style = style else { return } + switch style { + case .primary: + setPrimaryFacade() + + case .secondary: + setSecondaryFacade() + } + } + } + public var size: Styler.Button.Size? = .standard + public var groupName: String = "" + public var inverted: Bool = false + + public lazy var enabledColors: FacadeElements = (fill: enabled_fillColor(), + text: enabled_textColor(), + border: enabled_borderColor()) + + public lazy var disabledColors: FacadeElements = (fill: disabled_fillColor(), + text: disabled_textColor(), + border: disabled_borderColor()) + + public var enabledFillColor: Color? + public var enabledTextColor: Color? + public var enabledBorderColor: Color? + + public var enabledFillColor_inverted: Color? + public var enabledTextColor_inverted: Color? + public var enabledBorderColor_inverted: Color? + public var disabledFillColor: Color? public var disabledTextColor: Color? public var disabledBorderColor: Color? - public var groupName: String = "" - + + public var disabledFillColor_inverted: Color? + public var disabledTextColor_inverted: Color? + public var disabledBorderColor_inverted: Color? + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + public func setValidity(_ valid: Bool, group: FormGroupRule) { enabled = valid updateUI?() } - + /// Temporary binding mechanism for the view to update on enable changes. - public var updateUI: (() -> Void)? - + public var updateUI: ActionBlock? + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public init(with title: String, action: ActionModelProtocol) { self.title = title self.action = action @@ -52,71 +87,173 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW self.action = action style = .secondary } - + public init(primaryButtonWith title: String, action: ActionModelProtocol) { self.title = title self.action = action style = .primary } + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + + public func enabled_fillColor() -> UIColor? { + return (inverted ? enabledFillColor_inverted : enabledFillColor)?.uiColor + } + + public func enabled_textColor() -> UIColor? { + return (inverted ? enabledTextColor_inverted : enabledTextColor)?.uiColor + } + + public func enabled_borderColor() -> UIColor? { + return (inverted ? enabledBorderColor_inverted : enabledBorderColor)?.uiColor + } + + public func disabled_fillColor() -> UIColor? { + return (inverted ? disabledFillColor_inverted : disabledFillColor)?.uiColor + } + + public func disabled_textColor() -> UIColor? { + return (inverted ? disabledTextColor_inverted : disabledTextColor)?.uiColor + } + + public func disabled_borderColor() -> UIColor? { + return (inverted ? disabledBorderColor_inverted : disabledBorderColor)?.uiColor + } + + /// Defines the default appearance for the primary style. + func setPrimaryFacade() { + + if enabledFillColor == nil && enabledTextColor == nil { + enabledFillColor = Color(uiColor: .mvmBlack) + enabledTextColor = Color(uiColor: .mvmWhite) + } + + if disabledFillColor == nil && disabledTextColor == nil { + disabledFillColor = Color(uiColor: .mvmCoolGray6) + disabledTextColor = Color(uiColor: .mvmWhite) + } + + enabledFillColor_inverted = Color(uiColor: .mvmWhite) + enabledTextColor_inverted = Color(uiColor: .mvmBlack) + disabledFillColor_inverted = Color(uiColor: .mvmCoolGray6) + disabledTextColor_inverted = Color(uiColor: .mvmBlack) + } + + /// Defines the default appearance for the Secondary style. + func setSecondaryFacade() { + + if enabledTextColor == nil && enabledBorderColor == nil { + enabledTextColor = Color(uiColor: .mvmBlack) + enabledBorderColor = Color(uiColor: .mvmBlack) + } + + if disabledTextColor == nil && disabledBorderColor == nil { + disabledTextColor = Color(uiColor: .mvmCoolGray6) + disabledBorderColor = Color(uiColor: .mvmCoolGray6) + } + + enabledTextColor_inverted = Color(uiColor: .mvmWhite) + enabledBorderColor_inverted = Color(uiColor: .mvmWhite) + disabledTextColor_inverted = Color(uiColor: .mvmCoolGray6) + disabledBorderColor_inverted = Color(uiColor: .mvmCoolGray6) + } + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor case title + case inverted case action case enabled case style case size + case groupName case fillColor case textColor case borderColor case disabledFillColor case disabledTextColor case disabledBorderColor - case groupName } - + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) title = try typeContainer.decode(String.self, forKey: .title) action = try typeContainer.decodeModel(codingKey: .action) - if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { - self.groupName = groupName - } - if let style = try typeContainer.decodeIfPresent(ButtonStyle.self, forKey: .style) { + + if let style = try typeContainer.decodeIfPresent(Styler.Button.Style.self, forKey: .style) { self.style = style } - if let size = try typeContainer.decodeIfPresent(ButtonSize.self, forKey: .size) { + + if let size = try typeContainer.decodeIfPresent(Styler.Button.Size.self, forKey: .size) { self.size = size } + if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { self.enabled = enabled } - fillColor = try typeContainer.decodeIfPresent(Color.self, forKey: .fillColor) - textColor = try typeContainer.decodeIfPresent(Color.self, forKey: .textColor) - borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) - disabledFillColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledFillColor) - disabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledTextColor) - disabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBorderColor) + + if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) { + self.inverted = inverted + } + + if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { + self.groupName = groupName + } + + if let enabledFillColor = try typeContainer.decodeIfPresent(Color.self, forKey: .fillColor) { + self.enabledFillColor = enabledFillColor + } + + if let enabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .textColor) { + self.enabledTextColor = enabledTextColor + } + + if let enabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) { + self.enabledBorderColor = enabledBorderColor + } + + if let disabledFillColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledFillColor) { + self.disabledFillColor = disabledFillColor + } + + if let disabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledTextColor) { + self.disabledTextColor = disabledTextColor + } + + if let disabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBorderColor) { + self.disabledBorderColor = disabledBorderColor + } } - + public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) try container.encode(title, forKey: .title) - try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) - try container.encodeModel(action, forKey: .action) try container.encode(enabled, forKey: .enabled) - try container.encodeIfPresent(style, forKey: .style) - try container.encodeIfPresent(size, forKey: .size) - try container.encodeIfPresent(fillColor, forKey: .fillColor) - try container.encodeIfPresent(textColor, forKey: .textColor) - try container.encodeIfPresent(borderColor, forKey: .borderColor) + try container.encode(inverted, forKey: .inverted) + try container.encodeModel(action, forKey: .action) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(enabledFillColor, forKey: .fillColor) + try container.encodeIfPresent(enabledTextColor, forKey: .textColor) + try container.encodeIfPresent(enabledBorderColor, forKey: .borderColor) try container.encodeIfPresent(disabledFillColor, forKey: .disabledFillColor) try container.encodeIfPresent(disabledTextColor, forKey: .disabledTextColor) try container.encodeIfPresent(disabledBorderColor, forKey: .disabledBorderColor) + try container.encodeIfPresent(style, forKey: .style) + try container.encodeIfPresent(size, forKey: .size) try container.encodeIfPresent(groupName, forKey: .groupName) } } diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift index 19f032fc..745a34f4 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift @@ -8,80 +8,105 @@ import UIKit + open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { - // Used to size the button. + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + /// Used to size the button. var size = MVMCoreUIUtility.getWidth() var buttonModel: ButtonModel? { get { return model as? ButtonModel } } - // Need to re-style on set. + /// Need to re-style on set. open override var isEnabled: Bool { - didSet { - style() - } + didSet { style() } } - private enum ButtonHeight: CGFloat { - case tiny = 20 - case standard = 42 + //-------------------------------------------------- + // MARK: - Computed Properties + //-------------------------------------------------- + + public var enabledTitleColor: UIColor? { + get { return titleColor(for: .normal) } + set { setTitleColor(newValue, for: .normal) } } + public var disabledTitleColor: UIColor? { + get { return titleColor(for: .disabled) } + set { setTitleColor(newValue, for: .disabled) } + } + + public var borderColor: UIColor? { + get { + guard let currentColor = layer.borderColor else { return nil } + return UIColor(cgColor: currentColor) + } + set { layer.borderColor = newValue?.cgColor } + } + + //-------------------------------------------------- + // MARK: - Methods + //-------------------------------------------------- + /// The primary styling for a button. Should be used for main buttons public func stylePrimary() { - setTitleColor(.white, for: .normal) - setTitleColor(.white, for: .disabled) + + enabledTitleColor = buttonModel?.enabledColors.text ?? .mvmWhite + disabledTitleColor = buttonModel?.disabledColors.text ?? .mvmWhite layer.borderWidth = 0 - if isEnabled { - backgroundColor = .black - } else { - backgroundColor = .mvmCoolGray6 - } + backgroundColor = isEnabled ? buttonModel?.enabledColors.fill ?? .mvmBlack : buttonModel?.disabledColors.fill ?? .mvmCoolGray6 } /// The secondary styling for a button. Should be used for secondary buttons public func styleSecondary() { - setTitleColor(.black, for: .normal) - setTitleColor(.mvmCoolGray6, for: .disabled) + + enabledTitleColor = buttonModel?.enabledColors.text ?? .mvmBlack + disabledTitleColor = buttonModel?.disabledColors.text ?? .mvmCoolGray6 backgroundColor = .clear layer.borderWidth = 1 - if isEnabled { - layer.borderColor = UIColor.black.cgColor - } else { - layer.borderColor = UIColor.mvmCoolGray6.cgColor - } + borderColor = isEnabled ? buttonModel?.enabledColors.border ?? .mvmBlack : buttonModel?.disabledColors.border ?? .mvmCoolGray6 } /// Styles the button based on the model style private func style() { + switch buttonModel?.style { case .secondary: styleSecondary() + default: stylePrimary() } - if let titleColor = buttonModel?.textColor { - setTitleColor(titleColor.uiColor, for: .normal) + + if let titleColor = buttonModel?.enabledColors.text { + enabledTitleColor = titleColor } - if let disabledTitleColor = buttonModel?.disabledTextColor { - setTitleColor(disabledTitleColor.uiColor, for: .disabled) + + if let disabledTitleColor = buttonModel?.disabledColors.text { + self.disabledTitleColor = disabledTitleColor } + if isEnabled { - if let fillColor = buttonModel?.fillColor { - backgroundColor = fillColor.uiColor + if let fillColor = buttonModel?.enabledColors.fill { + backgroundColor = fillColor } - if let borderColor = buttonModel?.borderColor { + + if let borderColor = buttonModel?.enabledColors.border { layer.borderWidth = 1 - layer.borderColor = borderColor.cgColor + self.borderColor = borderColor } } else { - if let fillColor = buttonModel?.disabledFillColor { - backgroundColor = fillColor.uiColor + if let fillColor = buttonModel?.disabledColors.fill { + backgroundColor = fillColor } - if let borderColor = buttonModel?.disabledBorderColor { + + if let borderColor = buttonModel?.disabledColors.border { layer.borderWidth = 1 - layer.borderColor = borderColor.cgColor + self.borderColor = borderColor } } } @@ -94,38 +119,53 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { PillButton.getHeight(for: buttonModel?.size, size: size) } - public static func getHeight(for buttonSize: ButtonSize?, size: CGFloat) -> CGFloat { + public static func getHeight(for buttonSize: Styler.Button.Size?, size: CGFloat) -> CGFloat { + switch buttonSize { case .tiny: - return MFSizeObject(standardSize: ButtonHeight.tiny.rawValue, standardiPadPortraitSize: 34, iPadProLandscapeSize: 38)?.getValueBased(onSize: size) ?? ButtonHeight.tiny.rawValue + let tinyHeight = Styler.Button.Size.tiny.getHeight() + return MFSizeObject(standardSize: tinyHeight, + standardiPadPortraitSize: 34, + iPadProLandscapeSize: 38)?.getValueBased(onSize: size) ?? tinyHeight + default: - return MFSizeObject(standardSize: ButtonHeight.standard.rawValue, standardiPadPortraitSize: 46, iPadProLandscapeSize: 50)?.getValueBased(onSize: size) ?? ButtonHeight.standard.rawValue + let standardHeight = Styler.Button.Size.standard.getHeight() + return MFSizeObject(standardSize: standardHeight, + standardiPadPortraitSize: 46, + iPadProLandscapeSize: 50)?.getValueBased(onSize: size) ?? standardHeight } } - + private func getMinimumWidth() -> CGFloat { + switch buttonModel?.size { case .tiny: - return MFSizeObject(standardSize: 49.0, standardiPadPortraitSize: 90.0, iPadProLandscapeSize: 135.0)?.getValueBased(onSize: size) ?? 49.0 - default: - return 151.0 + return MFSizeObject(standardSize: 49, + standardiPadPortraitSize: 90, + iPadProLandscapeSize: 135)?.getValueBased(onSize: size) ?? 49 + + default: return 151 } } open override var intrinsicContentSize: CGSize { + let size = super.intrinsicContentSize let width = size.width + (2 * getInnerPadding()) return CGSize(width: max(width, getMinimumWidth()), height: getHeight()) } - // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + // MARK: - MVMCoreViewProtocol + //-------------------------------------------------- + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { // The button will get styled in the enable check in super. super.set(with: model, delegateObject, additionalData) - + guard let model = model as? ButtonModel else { return } setTitle(model.title, for: .normal) - + model.updateUI = { [weak self] in MVMCoreDispatchUtility.performBlock(onMainThread: { self?.enableField(model.enabled) @@ -134,39 +174,46 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) } - + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { PillButton.getHeight(for: (model as? ButtonModel)?.size, size: MVMCoreUIUtility.getWidth()) } - // MARK: - MVMCoreViewProtocol open override func updateView(_ size: CGFloat) { super.updateView(size) self.size = size + invalidateIntrinsicContentSize() + switch buttonModel?.size { case .tiny: - titleLabel?.font = MFFonts.mfFont75Bd(11 * (intrinsicContentSize.height / ButtonHeight.tiny.rawValue)) + titleLabel?.font = MFFonts.mfFont75Bd(11 * (intrinsicContentSize.height / Styler.Button.Size.tiny.getHeight())) + default: - titleLabel?.font = MFFonts.mfFont75Bd(13 * (intrinsicContentSize.height / ButtonHeight.standard.rawValue)) + titleLabel?.font = MFFonts.mfFont75Bd(13 * (intrinsicContentSize.height / Styler.Button.Size.standard.getHeight())) } + layer.cornerRadius = getInnerPadding() } - + open override func setupView() { super.setupView() + titleLabel?.numberOfLines = 1 titleLabel?.lineBreakMode = .byTruncatingTail titleLabel?.textAlignment = .center contentHorizontalAlignment = .center stylePrimary() } - + + //-------------------------------------------------- // MARK: - MVMCoreUIViewConstrainingProtocol + //-------------------------------------------------- + open func horizontalAlignment() -> UIStackView.Alignment { return .center } - + public func enableField(_ enable: Bool) { isEnabled = enable } diff --git a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinksModel.swift b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinksModel.swift index 41d4ec72..de9e5d63 100644 --- a/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinksModel.swift +++ b/MVMCoreUI/Atomic/Molecules/DesignedComponents/List/RightVariable/ListRightVariableButtonAllTextAndLinksModel.swift @@ -22,7 +22,7 @@ public class ListRightVariableButtonAllTextAndLinksModel: ListItemModel, Molecul override public func setDefaults() { super.setDefaults() self.button.size = .tiny - self.button.style = ButtonStyle.secondary + self.button.style = .secondary } private enum CodingKeys: String, CodingKey { diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonView.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonView.swift index 285c429c..31420e56 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonView.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonView.swift @@ -9,11 +9,24 @@ import UIKit @objcMembers open class TwoButtonView: View, MVMCoreUIViewConstrainingProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + open var primaryButton: PillButton = PillButton() open var secondaryButton: PillButton = PillButton() private var stack = UIStackView() + + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + private var equalWidthConstraint: NSLayoutConstraint? - + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public init() { super.init(frame: .zero) } @@ -26,16 +39,21 @@ import UIKit super.init(frame: frame) } - public func setDefault() { + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + public func setDefaultAppearance() { + primaryButton.stylePrimary() secondaryButton.styleSecondary() } - // MARK: - MVMCoreViewProtocol open override func updateView(_ size: CGFloat) { super.updateView(size) - self.primaryButton.updateView(size) - self.secondaryButton.updateView(size) + + primaryButton.updateView(size) + secondaryButton.updateView(size) } open override func setupView() { @@ -52,58 +70,70 @@ import UIKit equalWidthConstraint?.isActive = true } + //-------------------------------------------------- // MARK: - Stack Manipulation + //-------------------------------------------------- + public func showPrimaryButton() { + if !stack.arrangedSubviews.contains(primaryButton) { stack.addArrangedSubview(primaryButton) primaryButton.isHidden = false } + if secondaryButton.superview != nil { equalWidthConstraint?.isActive = true } } public func showSecondaryButton() { + if !stack.arrangedSubviews.contains(secondaryButton) { stack.insertArrangedSubview(secondaryButton, at: 0) secondaryButton.isHidden = false } + if primaryButton.superview != nil { equalWidthConstraint?.isActive = true } } public func hidePrimaryButton() { + if primaryButton.superview != nil { stack.removeArrangedSubview(primaryButton) primaryButton.isHidden = true } + equalWidthConstraint?.isActive = false } public func hideSecondaryButton() { + if secondaryButton.superview != nil { stack.removeArrangedSubview(secondaryButton) secondaryButton.isHidden = true } + equalWidthConstraint?.isActive = false } + //-------------------------------------------------- // MARK: - MoleculeViewProtocol + //-------------------------------------------------- + open override func reset() { super.reset() - setDefault() - } - - // MARK: - MVMCoreUIViewConstrainingProtocol - open func horizontalAlignment() -> UIStackView.Alignment { - return .center + + setDefaultAppearance() } - // MARK: - MoleculeViewProtocol public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + guard let model = model as? TwoButtonViewModel, - let buttonModel = model.primaryButton ?? model.secondaryButton else { return 0 } + let buttonModel = model.primaryButton ?? model.secondaryButton + else { return 0 } + return PillButton.estimatedHeight(with: buttonModel, delegateObject) } @@ -118,6 +148,7 @@ import UIKit } else { hideSecondaryButton() } + if let primaryModel = model.primaryButton { showPrimaryButton() primaryButton.set(with: primaryModel, delegateObject, additionalData) @@ -125,4 +156,11 @@ import UIKit hidePrimaryButton() } } + //-------------------------------------------------- + // MARK: - MVMCoreUIViewConstrainingProtocol + //-------------------------------------------------- + + open func horizontalAlignment() -> UIStackView.Alignment { + return .center + } } diff --git a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonViewModel.swift b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonViewModel.swift index 057203e9..d4da0ba4 100644 --- a/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonViewModel.swift +++ b/MVMCoreUI/Atomic/Molecules/HorizontalCombinationViews/TwoButtonViewModel.swift @@ -8,35 +8,54 @@ import UIKit + public class TwoButtonViewModel: MoleculeModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public static var identifier: String = "twoButtonView" public var backgroundColor: Color? public var primaryButton: ButtonModel? public var secondaryButton: ButtonModel? + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor case primaryButton case secondaryButton } - + + //-------------------------------------------------- + // MARK: - Initialzer + //-------------------------------------------------- + public init(_ primaryButton: ButtonModel?, _ secondaryButton: ButtonModel?) { self.primaryButton = primaryButton self.secondaryButton = secondaryButton } + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) primaryButton = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .primaryButton) + if primaryButton?.style == nil { + primaryButton?.style = .primary + } secondaryButton = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .secondaryButton) - // Default value if secondaryButton?.style == nil { secondaryButton?.style = .secondary } } - + public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(moleculeName, forKey: .moleculeName) diff --git a/MVMCoreUI/Atomic/Templates/TemplateModel.swift b/MVMCoreUI/Atomic/Templates/TemplateModel.swift index 86da1981..3dcf3964 100644 --- a/MVMCoreUI/Atomic/Templates/TemplateModel.swift +++ b/MVMCoreUI/Atomic/Templates/TemplateModel.swift @@ -8,24 +8,39 @@ import Foundation + @objcMembers public class TemplateModel: MVMControllerModelProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public class var identifier: String { return "" } + public var pageType: String public var template: String { // Although this is done in the extension, it is needed for the encoding. return Self.identifier } + public var backgroundColor: Color? public var screenHeading: String? public var navigationItem: (NavigationItemModelProtocol & MoleculeModelProtocol)? public var formRules: [FormGroupRule]? + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + public init(pageType: String) { self.pageType = pageType } + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + private enum CodingKeys: String, CodingKey { case pageType case template @@ -34,6 +49,10 @@ import Foundation case formRules case navigationItem } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) diff --git a/MVMCoreUI/Atomic/Templates/ThreeLayerTemplate.swift b/MVMCoreUI/Atomic/Templates/ThreeLayerTemplate.swift index c393297b..f1b2633c 100644 --- a/MVMCoreUI/Atomic/Templates/ThreeLayerTemplate.swift +++ b/MVMCoreUI/Atomic/Templates/ThreeLayerTemplate.swift @@ -24,25 +24,25 @@ import UIKit open override func viewForTop() -> UIView? { guard let headerModel = templateModel?.header, - let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar) else { - return nil - } + let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar) + else { return nil } + return molecule } open override func viewForMiddle() -> UIView? { guard let middleModel = templateModel?.middle, - let molecule = MoleculeObjectMapping.shared()?.createMolecule(middleModel, delegateObject: delegateObjectIVar) else { - return nil - } + let molecule = MoleculeObjectMapping.shared()?.createMolecule(middleModel, delegateObject: delegateObjectIVar) + else { return nil } + return molecule } override open func viewForBottom() -> UIView? { guard let footerModel = templateModel?.footer, - let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar) else { - return nil - } + let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar) + else { return nil } + return molecule } diff --git a/MVMCoreUI/BaseClasses/TextViewModel.swift b/MVMCoreUI/BaseClasses/TextViewModel.swift index 49ac664c..d9505275 100644 --- a/MVMCoreUI/BaseClasses/TextViewModel.swift +++ b/MVMCoreUI/BaseClasses/TextViewModel.swift @@ -81,6 +81,7 @@ open class TextViewModel: TextEntryFieldModel { } public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) try container.encodeIfPresent(height, forKey: .height) diff --git a/MVMCoreUI/BaseControllers/ViewController.swift b/MVMCoreUI/BaseControllers/ViewController.swift index 75cb897c..e9310547 100644 --- a/MVMCoreUI/BaseControllers/ViewController.swift +++ b/MVMCoreUI/BaseControllers/ViewController.swift @@ -258,7 +258,7 @@ import UIKit viewRespectsSystemMinimumLayoutMargins = false // Presents from the bottom. - modalPresentationStyle = MVMCoreGetterUtility.isOnIPad() ? UIModalPresentationStyle.formSheet : UIModalPresentationStyle.overCurrentContext + modalPresentationStyle = MVMCoreGetterUtility.isOnIPad() ? .formSheet : .overCurrentContext // Create the default delegate object. delegateObjectIVar = MVMCoreUIDelegateObject.create(withDelegateForAll: self) @@ -339,18 +339,18 @@ import UIKit } // MARK: - MVMCoreActionDelegateProtocol - open func handleOpenPage(for requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) { + open func handleOpenPage(for requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?) { formValidator?.addFormParams(requestParameters: requestParameters) requestParameters.parentPageType = loadObject?.pageJSON?.optionalStringForKey("parentPageType") MVMCoreActionHandler.defaultHandleOpenPage(for: requestParameters, additionalData: additionalData, delegateObject: delegateObject()) } - open func logAction(withActionInformation actionInformation: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) { + open func logAction(withActionInformation actionInformation: [AnyHashable: Any]?, additionalData: [AnyHashable: Any]?) { MVMCoreUILoggingHandler.shared()?.defaultLogAction(forController: self, actionInformation: actionInformation, additionalData: additionalData) } // MARK: - MoleculeDelegateProtocol - open func getModuleWithName(_ name: String?) -> [AnyHashable : Any]? { + open func getModuleWithName(_ name: String?) -> [AnyHashable: Any]? { guard let name = name else { return nil } return loadObject?.modulesJSON?.optionalDictionaryForKey(name) } @@ -391,7 +391,7 @@ import UIKit view.accessibilityElements = [pickerView, toolBar] } DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - UIAccessibility.post(notification: UIAccessibility.Notification.layoutChanged, argument: textField.inputView) + UIAccessibility.post(notification: .layoutChanged, argument: textField.inputView) } } } @@ -400,7 +400,7 @@ import UIKit if textField === selectedField { if UIAccessibility.isVoiceOverRunning { view.accessibilityElements = nil - UIAccessibility.post(notification: UIAccessibility.Notification.layoutChanged, argument: textField) + UIAccessibility.post(notification: .layoutChanged, argument: textField) } selectedField = nil } diff --git a/MVMCoreUI/Containers/Views/Container.swift b/MVMCoreUI/Containers/Views/Container.swift index eb05cd21..8c95f110 100644 --- a/MVMCoreUI/Containers/Views/Container.swift +++ b/MVMCoreUI/Containers/Views/Container.swift @@ -9,8 +9,10 @@ import UIKit open class Container: View, ContainerProtocol { + public var view: UIView? let containerHelper = ContainerHelper() + var containerModel: ContainerModelProtocol? { get { return model as? ContainerModelProtocol } } diff --git a/MVMCoreUI/CustomPrimitives/Color.swift b/MVMCoreUI/CustomPrimitives/Color.swift index f06b337c..42c7a600 100644 --- a/MVMCoreUI/CustomPrimitives/Color.swift +++ b/MVMCoreUI/CustomPrimitives/Color.swift @@ -55,6 +55,13 @@ public final class Color: Codable { determineRGBA() } + public init?(uiColor: UIColor?) { + guard let uiColor = uiColor else { return nil } + self.uiColor = uiColor + hex = UIColor.hexString(for: uiColor) ?? "" + determineRGBA() + } + init?(name: String) { guard let colorTuple = UIColor.names[name] else { return nil } self.uiColor = colorTuple.uiColor diff --git a/MVMCoreUI/Styles/Styler.swift b/MVMCoreUI/Styles/Styler.swift index 863d4703..d8d0a519 100644 --- a/MVMCoreUI/Styles/Styler.swift +++ b/MVMCoreUI/Styles/Styler.swift @@ -172,6 +172,29 @@ open class Styler { } } + public enum Button { + + public enum Style: String, Codable { + case primary + case secondary + } + + public enum Size: String, Codable { + case standard + case tiny + + func getHeight() -> CGFloat { + switch self { + case .standard: + return 42 + + case .tiny: + return 20 + } + } + } + } + //-------------------------------------------------- // MARK: - Functions //--------------------------------------------------