diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift index 3b72d37c..fae40f0c 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/ButtonModel.swift @@ -7,6 +7,7 @@ // import UIKit +import VDSColorTokens public typealias FacadeElements = (fill: UIColor?, text: UIColor?, border: UIColor?) @@ -17,12 +18,12 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat //-------------------------------------------------- //Making static property as class property so that subclasses can override getter function of the property open class var identifier: String { "button" } - public var backgroundColor: Color? public var accessibilityIdentifier: String? public var accessibilityText: String? public var title: String public var action: ActionModelProtocol public var enabled: Bool = true + public var width: CGFloat? public var style: Styler.Button.Style? { didSet { guard let style = style else { return } @@ -57,6 +58,20 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat public var disabledTextColor_inverted: Color? public var disabledBorderColor_inverted: Color? + private var _backgroundColor: Color? + public var backgroundColor: Color? { + get { + if let backgroundColor = _backgroundColor { return backgroundColor } + if inverted { + return enabled ? enabledFillColor_inverted : disabledFillColor_inverted + } + return enabled ? enabledFillColor : disabledFillColor + } + set { + _backgroundColor = newValue + } + } + //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- @@ -70,18 +85,21 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat public init(with title: String, action: ActionModelProtocol) { self.title = title self.action = action + setFacade(by: .primary) } public init(secondaryButtonWith title: String, action: ActionModelProtocol) { self.title = title self.action = action style = .secondary + setFacade(by: .secondary) } public init(primaryButtonWith title: String, action: ActionModelProtocol) { self.title = title self.action = action style = .primary + setFacade(by: .primary) } //-------------------------------------------------- @@ -114,40 +132,30 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat /// Defines the default appearance for the primary style. func setPrimaryFacade() { + enabledFillColor = Color(uiColor: VDSColor.elementsPrimaryOnlight) + enabledTextColor = Color(uiColor: VDSColor.elementsPrimaryOndark) + disabledFillColor = Color(uiColor: VDSColor.interactiveDisabledOnlight) + disabledTextColor = Color(uiColor: VDSColor.elementsPrimaryOndark) - 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) + enabledFillColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOndark) + enabledTextColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOnlight) + disabledFillColor_inverted = Color(uiColor: VDSColor.interactiveDisabledOndark) + disabledTextColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOnlight) } /// Defines the default appearance for the Secondary style. func setSecondaryFacade() { + enabledTextColor = Color(uiColor: VDSColor.elementsPrimaryOnlight) + enabledFillColor = Color(uiColor: UIColor.clear) + enabledBorderColor = Color(uiColor: VDSColor.elementsPrimaryOnlight) + disabledTextColor = Color(uiColor: VDSColor.interactiveDisabledOnlight) + disabledBorderColor = Color(uiColor: VDSColor.interactiveDisabledOnlight) - 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) + enabledTextColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOndark) + enabledFillColor_inverted = Color(uiColor: UIColor.clear) + enabledBorderColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOndark) + disabledTextColor_inverted = Color(uiColor: VDSColor.interactiveDisabledOndark) + disabledBorderColor_inverted = Color(uiColor: VDSColor.interactiveDisabledOndark) } public func setFacade(by style: Styler.Button.Style) { @@ -183,6 +191,7 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat case disabledFillColor case disabledTextColor case disabledBorderColor + case width } //-------------------------------------------------- @@ -192,7 +201,6 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) title = try typeContainer.decode(String.self, forKey: .title) @@ -201,6 +209,8 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat if let style = try typeContainer.decodeIfPresent(Styler.Button.Style.self, forKey: .style) { self.style = style setFacade(by: style) + } else { + setFacade(by: .primary) } if let size = try typeContainer.decodeIfPresent(Styler.Button.Size.self, forKey: .size) { @@ -242,6 +252,9 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat if let disabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBorderColor) { self.disabledBorderColor = disabledBorderColor } + + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + width = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .width) } open func encode(to encoder: Encoder) throws { @@ -251,7 +264,7 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat try container.encode(enabled, forKey: .enabled) try container.encode(inverted, forKey: .inverted) try container.encodeModel(action, forKey: .action) - try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(_backgroundColor, forKey: .backgroundColor) try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) try container.encodeIfPresent(enabledFillColor, forKey: .fillColor) @@ -263,5 +276,6 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat try container.encodeIfPresent(style, forKey: .style) try container.encodeIfPresent(size, forKey: .size) try container.encodeIfPresent(groupName, forKey: .groupName) + try container.encodeIfPresent(width, forKey: .width) } } diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift index 5b8f75cb..6b47f112 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/PillButton.swift @@ -7,7 +7,7 @@ // import UIKit - +import VDSColorTokens open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { //-------------------------------------------------- @@ -23,21 +23,29 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { /// Need to re-style on set. open override var isEnabled: Bool { - didSet { style() } + didSet { style(with: buttonModel) } } open var buttonSize: Styler.Button.Size = .standard { didSet { buttonModel?.size = buttonSize } } + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + + public var widthConstraint: NSLayoutConstraint? + public var minimumWidthConstraint: NSLayoutConstraint? + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @objc public convenience init(asPrimaryButton isPrimary: Bool, makeTiny istiny: Bool) { - self.init() - buttonSize = istiny ? .tiny : .standard - isPrimary ? stylePrimary() : styleSecondary() + let model = ButtonModel(with: "", action: ActionNoopModel()) + model.style = isPrimary ? .primary : .secondary + model.size = istiny ? .tiny : .standard + self.init(model: model, nil, nil) } //-------------------------------------------------- @@ -68,39 +76,26 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { /// The primary styling for a button. Should be used for main buttons public func stylePrimary() { - - enabledTitleColor = buttonModel?.enabledColors.text ?? .mvmWhite - disabledTitleColor = buttonModel?.disabledColors.text ?? .mvmWhite - layer.borderWidth = 0 - backgroundColor = isEnabled ? buttonModel?.enabledColors.fill ?? .mvmBlack : buttonModel?.disabledColors.fill ?? .mvmCoolGray6 + let buttonModel = ButtonModel(primaryButtonWith: "", action: ActionNoopModel()) + style(with: buttonModel) } /// The secondary styling for a button. Should be used for secondary buttons public func styleSecondary() { - - enabledTitleColor = buttonModel?.enabledColors.text ?? .mvmBlack - disabledTitleColor = buttonModel?.disabledColors.text ?? .mvmCoolGray6 - backgroundColor = .clear - layer.borderWidth = 1 - borderColor = isEnabled ? buttonModel?.enabledColors.border ?? .mvmBlack : buttonModel?.disabledColors.border ?? .mvmCoolGray6 + let buttonModel = ButtonModel(secondaryButtonWith: "", action: ActionNoopModel()) + style(with: buttonModel) } /// Styles the button based on the model style - private func style() { + private func style(with model: ButtonModel?) { - switch buttonModel?.style { - case .secondary: - styleSecondary() - - default: - stylePrimary() - } + layer.borderWidth = model?.style == .secondary ? 1 : 0 - if let titleColor = buttonModel?.enabledColors.text { + if let titleColor = model?.enabledColors.text { enabledTitleColor = titleColor } - if let disabledTitleColor = buttonModel?.disabledColors.text { + if let disabledTitleColor = model?.disabledColors.text { self.disabledTitleColor = disabledTitleColor } @@ -110,72 +105,46 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { #endif if isEnabled { - if let fillColor = buttonModel?.enabledColors.fill { + if let fillColor = model?.enabledColors.fill { backgroundColor = fillColor } - if let borderColor = buttonModel?.enabledColors.border { - layer.borderWidth = 1 + if let borderColor = model?.enabledColors.border { self.borderColor = borderColor } } else { - if let fillColor = buttonModel?.disabledColors.fill { + if let fillColor = model?.disabledColors.fill { backgroundColor = fillColor } - if let borderColor = buttonModel?.disabledColors.border { - layer.borderWidth = 1 + if let borderColor = model?.disabledColors.border { self.borderColor = borderColor } } } private func getInnerPadding() -> CGFloat { - getHeight() / 2.0 + buttonSize.getHeight() / 2.0 } - - private func getHeight() -> CGFloat { - PillButton.getHeight(for: buttonSize, size: size) - } - - public static func getHeight(for buttonSize: Styler.Button.Size?, size: CGFloat) -> CGFloat { + private func getContentEdgeInsets() -> UIEdgeInsets { + var verticalPadding = 0.0 + var horizontalPadding = 0.0 switch buttonSize { + case .standard: + verticalPadding = Padding.Three + horizontalPadding = Padding.Five + break + case .small: + verticalPadding = Padding.Two + horizontalPadding = Padding.Four + break case .tiny: - let tinyHeight = Styler.Button.Size.tiny.getHeight() - return MFSizeObject(standardSize: tinyHeight, - standardiPadPortraitSize: 34, - iPadProLandscapeSize: 38)?.getValueBased(onSize: size) ?? tinyHeight - - default: - let standardHeight = Styler.Button.Size.standard.getHeight() - return MFSizeObject(standardSize: standardHeight, - standardiPadPortraitSize: 46, - iPadProLandscapeSize: 50)?.getValueBased(onSize: size) ?? standardHeight - } - } - - private func getMinimumWidth() -> CGFloat { - - switch buttonSize { - case .tiny: - return MFSizeObject(standardSize: 49, - standardiPadPortraitSize: 90, - iPadProLandscapeSize: 135)?.getValueBased(onSize: size) ?? 49 - - default: return 151 - } - } - - open override var intrinsicContentSize: CGSize { - if buttonSize == .tiny { - let size = super.intrinsicContentSize - let width = size.width + (2 * getInnerPadding()) - return CGSize(width: max(width, getMinimumWidth()), height: getHeight()) - } else { - let width = Padding.Component.gutterForApplicationWidth + (2.0 * Padding.Component.columnFor(size: MVMCoreUISplitViewController.getApplicationViewWidth())) - return CGSize(width: min(292, width), height: getHeight()) + verticalPadding = Padding.One + horizontalPadding = Padding.Two + break } + return UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding) } //-------------------------------------------------- @@ -187,6 +156,7 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { super.set(with: model, delegateObject, additionalData) guard let model = model as? ButtonModel else { return } + setTitle(model.title, for: .normal) if let accessibilityText = model.accessibilityText { accessibilityLabel = accessibilityText @@ -206,24 +176,44 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { } open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - PillButton.getHeight(for: (model as? ButtonModel)?.size, size: MVMCoreUIUtility.getWidth()) + return (model as? ButtonModel)?.size?.getHeight() } open override func updateView(_ size: CGFloat) { super.updateView(size) self.size = size - invalidateIntrinsicContentSize() - switch buttonSize { case .tiny: - titleLabel?.font = MFFonts.mfFont75Bd(11 * (intrinsicContentSize.height / Styler.Button.Size.tiny.getHeight())) - - default: - titleLabel?.font = MFFonts.mfFont75Bd(13 * (intrinsicContentSize.height / Styler.Button.Size.standard.getHeight())) + titleLabel?.font = Styler.Font.BoldMicro.getFont() + case .small: + titleLabel?.font = Styler.Font.BoldBodySmall.getFont() + case .standard: + titleLabel?.font = Styler.Font.BoldBodyLarge.getFont() } layer.cornerRadius = getInnerPadding() + contentEdgeInsets = getContentEdgeInsets() + + if let contraint = buttonModel?.width { + + if widthConstraint == nil { + widthConstraint = widthAnchor.constraint(equalToConstant: contraint) + } else if widthConstraint?.constant != contraint { + widthConstraint?.constant = contraint + } + widthConstraint?.isActive = true + minimumWidthConstraint?.isActive = false + } else { + + if minimumWidthConstraint == nil { + minimumWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: buttonSize.minimumWidth()) + } else { + minimumWidthConstraint?.constant = buttonSize.minimumWidth() + } + minimumWidthConstraint?.isActive = true + widthConstraint?.isActive = false + } } open override func setupView() { diff --git a/MVMCoreUI/Styles/Styler.swift b/MVMCoreUI/Styles/Styler.swift index 4da119fc..9cc487e8 100644 --- a/MVMCoreUI/Styles/Styler.swift +++ b/MVMCoreUI/Styles/Styler.swift @@ -178,20 +178,33 @@ open class Styler { case primary case secondary } - + ///MVA 3.0 - Button sizes are standard(default size), small, Tiny. Tiny button has been depricated as of Rebranding 3.0. public enum Size: String, Codable { case standard + case small case tiny func getHeight() -> CGFloat { switch self { case .standard: - return 42 - + return 44 + case .small: + return 32 case .tiny: return 20 } } + + func minimumWidth() -> CGFloat { + switch self { + case .standard: + return 76 + case .small: + return 0 + case .tiny: + return 49 + } + } } } diff --git a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m index fa96630f..d2a009c9 100644 --- a/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m +++ b/MVMCoreUI/TopAlert/MVMCoreUITopAlertMainView.m @@ -204,7 +204,6 @@ // Sets up to use a button action. Always uses the top view controller PillButton *button = [[PillButton alloc] initAsPrimaryButton:false makeTiny:true]; - [button styleSecondary]; [button setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal]; [button setContentHuggingPriority:800 forAxis:UILayoutConstraintAxisHorizontal];