Merge branch 'feature/VDS_Button' into 'feature/develop_mvp_3'

Feature/vds button

See merge request BPHV_MIPS/mvm_core_ui!821
This commit is contained in:
Pfeil, Scott Robert 2022-05-09 17:54:36 +00:00
commit 0492a3fa5d
4 changed files with 130 additions and 114 deletions

View File

@ -7,6 +7,7 @@
// //
import UIKit import UIKit
import VDSColorTokens
public typealias FacadeElements = (fill: UIColor?, text: UIColor?, border: UIColor?) 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 //Making static property as class property so that subclasses can override getter function of the property
open class var identifier: String { "button" } open class var identifier: String { "button" }
public var backgroundColor: Color?
public var accessibilityIdentifier: String? public var accessibilityIdentifier: String?
public var accessibilityText: String? public var accessibilityText: String?
public var title: String public var title: String
public var action: ActionModelProtocol public var action: ActionModelProtocol
public var enabled: Bool = true public var enabled: Bool = true
public var width: CGFloat?
public var style: Styler.Button.Style? { public var style: Styler.Button.Style? {
didSet { didSet {
guard let style = style else { return } guard let style = style else { return }
@ -57,6 +58,20 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
public var disabledTextColor_inverted: Color? public var disabledTextColor_inverted: Color?
public var disabledBorderColor_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 // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
@ -70,18 +85,21 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
public init(with title: String, action: ActionModelProtocol) { public init(with title: String, action: ActionModelProtocol) {
self.title = title self.title = title
self.action = action self.action = action
setFacade(by: .primary)
} }
public init(secondaryButtonWith title: String, action: ActionModelProtocol) { public init(secondaryButtonWith title: String, action: ActionModelProtocol) {
self.title = title self.title = title
self.action = action self.action = action
style = .secondary style = .secondary
setFacade(by: .secondary)
} }
public init(primaryButtonWith title: String, action: ActionModelProtocol) { public init(primaryButtonWith title: String, action: ActionModelProtocol) {
self.title = title self.title = title
self.action = action self.action = action
style = .primary style = .primary
setFacade(by: .primary)
} }
//-------------------------------------------------- //--------------------------------------------------
@ -114,40 +132,30 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
/// Defines the default appearance for the primary style. /// Defines the default appearance for the primary style.
func setPrimaryFacade() { 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_inverted = Color(uiColor: VDSColor.elementsPrimaryOndark)
enabledFillColor = Color(uiColor: .mvmBlack) enabledTextColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOnlight)
enabledTextColor = Color(uiColor: .mvmWhite) disabledFillColor_inverted = Color(uiColor: VDSColor.interactiveDisabledOndark)
} disabledTextColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOnlight)
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. /// Defines the default appearance for the Secondary style.
func setSecondaryFacade() { 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_inverted = Color(uiColor: VDSColor.elementsPrimaryOndark)
enabledTextColor = Color(uiColor: .mvmBlack) enabledFillColor_inverted = Color(uiColor: UIColor.clear)
enabledBorderColor = Color(uiColor: .mvmBlack) enabledBorderColor_inverted = Color(uiColor: VDSColor.elementsPrimaryOndark)
} disabledTextColor_inverted = Color(uiColor: VDSColor.interactiveDisabledOndark)
disabledBorderColor_inverted = Color(uiColor: VDSColor.interactiveDisabledOndark)
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)
} }
public func setFacade(by style: Styler.Button.Style) { public func setFacade(by style: Styler.Button.Style) {
@ -183,6 +191,7 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
case disabledFillColor case disabledFillColor
case disabledTextColor case disabledTextColor
case disabledBorderColor case disabledBorderColor
case width
} }
//-------------------------------------------------- //--------------------------------------------------
@ -192,7 +201,6 @@ open class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWat
required public init(from decoder: Decoder) throws { required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self) let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier)
accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
title = try typeContainer.decode(String.self, forKey: .title) 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) { if let style = try typeContainer.decodeIfPresent(Styler.Button.Style.self, forKey: .style) {
self.style = style self.style = style
setFacade(by: style) setFacade(by: style)
} else {
setFacade(by: .primary)
} }
if let size = try typeContainer.decodeIfPresent(Styler.Button.Size.self, forKey: .size) { 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) { if let disabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBorderColor) {
self.disabledBorderColor = 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 { 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(enabled, forKey: .enabled)
try container.encode(inverted, forKey: .inverted) try container.encode(inverted, forKey: .inverted)
try container.encodeModel(action, forKey: .action) 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(accessibilityIdentifier, forKey: .accessibilityIdentifier)
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
try container.encodeIfPresent(enabledFillColor, forKey: .fillColor) 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(style, forKey: .style)
try container.encodeIfPresent(size, forKey: .size) try container.encodeIfPresent(size, forKey: .size)
try container.encodeIfPresent(groupName, forKey: .groupName) try container.encodeIfPresent(groupName, forKey: .groupName)
try container.encodeIfPresent(width, forKey: .width)
} }
} }

View File

@ -7,7 +7,7 @@
// //
import UIKit import UIKit
import VDSColorTokens
open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
//-------------------------------------------------- //--------------------------------------------------
@ -23,21 +23,29 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
/// Need to re-style on set. /// Need to re-style on set.
open override var isEnabled: Bool { open override var isEnabled: Bool {
didSet { style() } didSet { style(with: buttonModel) }
} }
open var buttonSize: Styler.Button.Size = .standard { open var buttonSize: Styler.Button.Size = .standard {
didSet { buttonModel?.size = buttonSize } didSet { buttonModel?.size = buttonSize }
} }
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
public var widthConstraint: NSLayoutConstraint?
public var minimumWidthConstraint: NSLayoutConstraint?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
@objc public convenience init(asPrimaryButton isPrimary: Bool, makeTiny istiny: Bool) { @objc public convenience init(asPrimaryButton isPrimary: Bool, makeTiny istiny: Bool) {
self.init() let model = ButtonModel(with: "", action: ActionNoopModel())
buttonSize = istiny ? .tiny : .standard model.style = isPrimary ? .primary : .secondary
isPrimary ? stylePrimary() : styleSecondary() 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 /// The primary styling for a button. Should be used for main buttons
public func stylePrimary() { public func stylePrimary() {
let buttonModel = ButtonModel(primaryButtonWith: "", action: ActionNoopModel())
enabledTitleColor = buttonModel?.enabledColors.text ?? .mvmWhite style(with: buttonModel)
disabledTitleColor = buttonModel?.disabledColors.text ?? .mvmWhite
layer.borderWidth = 0
backgroundColor = isEnabled ? buttonModel?.enabledColors.fill ?? .mvmBlack : buttonModel?.disabledColors.fill ?? .mvmCoolGray6
} }
/// The secondary styling for a button. Should be used for secondary buttons /// The secondary styling for a button. Should be used for secondary buttons
public func styleSecondary() { public func styleSecondary() {
let buttonModel = ButtonModel(secondaryButtonWith: "", action: ActionNoopModel())
enabledTitleColor = buttonModel?.enabledColors.text ?? .mvmBlack style(with: buttonModel)
disabledTitleColor = buttonModel?.disabledColors.text ?? .mvmCoolGray6
backgroundColor = .clear
layer.borderWidth = 1
borderColor = isEnabled ? buttonModel?.enabledColors.border ?? .mvmBlack : buttonModel?.disabledColors.border ?? .mvmCoolGray6
} }
/// Styles the button based on the model style /// Styles the button based on the model style
private func style() { private func style(with model: ButtonModel?) {
switch buttonModel?.style { layer.borderWidth = model?.style == .secondary ? 1 : 0
case .secondary:
styleSecondary()
default:
stylePrimary()
}
if let titleColor = buttonModel?.enabledColors.text { if let titleColor = model?.enabledColors.text {
enabledTitleColor = titleColor enabledTitleColor = titleColor
} }
if let disabledTitleColor = buttonModel?.disabledColors.text { if let disabledTitleColor = model?.disabledColors.text {
self.disabledTitleColor = disabledTitleColor self.disabledTitleColor = disabledTitleColor
} }
@ -110,72 +105,46 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
#endif #endif
if isEnabled { if isEnabled {
if let fillColor = buttonModel?.enabledColors.fill { if let fillColor = model?.enabledColors.fill {
backgroundColor = fillColor backgroundColor = fillColor
} }
if let borderColor = buttonModel?.enabledColors.border { if let borderColor = model?.enabledColors.border {
layer.borderWidth = 1
self.borderColor = borderColor self.borderColor = borderColor
} }
} else { } else {
if let fillColor = buttonModel?.disabledColors.fill { if let fillColor = model?.disabledColors.fill {
backgroundColor = fillColor backgroundColor = fillColor
} }
if let borderColor = buttonModel?.disabledColors.border { if let borderColor = model?.disabledColors.border {
layer.borderWidth = 1
self.borderColor = borderColor self.borderColor = borderColor
} }
} }
} }
private func getInnerPadding() -> CGFloat { 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 { switch buttonSize {
case .standard:
verticalPadding = Padding.Three
horizontalPadding = Padding.Five
break
case .small:
verticalPadding = Padding.Two
horizontalPadding = Padding.Four
break
case .tiny: case .tiny:
let tinyHeight = Styler.Button.Size.tiny.getHeight() verticalPadding = Padding.One
return MFSizeObject(standardSize: tinyHeight, horizontalPadding = Padding.Two
standardiPadPortraitSize: 34, break
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())
} }
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) super.set(with: model, delegateObject, additionalData)
guard let model = model as? ButtonModel else { return } guard let model = model as? ButtonModel else { return }
setTitle(model.title, for: .normal) setTitle(model.title, for: .normal)
if let accessibilityText = model.accessibilityText { if let accessibilityText = model.accessibilityText {
accessibilityLabel = accessibilityText accessibilityLabel = accessibilityText
@ -206,24 +176,44 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
} }
open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { 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) { open override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
self.size = size self.size = size
invalidateIntrinsicContentSize()
switch buttonSize { switch buttonSize {
case .tiny: case .tiny:
titleLabel?.font = MFFonts.mfFont75Bd(11 * (intrinsicContentSize.height / Styler.Button.Size.tiny.getHeight())) titleLabel?.font = Styler.Font.BoldMicro.getFont()
case .small:
default: titleLabel?.font = Styler.Font.BoldBodySmall.getFont()
titleLabel?.font = MFFonts.mfFont75Bd(13 * (intrinsicContentSize.height / Styler.Button.Size.standard.getHeight())) case .standard:
titleLabel?.font = Styler.Font.BoldBodyLarge.getFont()
} }
layer.cornerRadius = getInnerPadding() 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() { open override func setupView() {

View File

@ -178,20 +178,33 @@ open class Styler {
case primary case primary
case secondary 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 { public enum Size: String, Codable {
case standard case standard
case small
case tiny case tiny
func getHeight() -> CGFloat { func getHeight() -> CGFloat {
switch self { switch self {
case .standard: case .standard:
return 42 return 44
case .small:
return 32
case .tiny: case .tiny:
return 20 return 20
} }
} }
func minimumWidth() -> CGFloat {
switch self {
case .standard:
return 76
case .small:
return 0
case .tiny:
return 49
}
}
} }
} }

View File

@ -204,7 +204,6 @@
// Sets up to use a button action. Always uses the top view controller // Sets up to use a button action. Always uses the top view controller
PillButton *button = [[PillButton alloc] initAsPrimaryButton:false makeTiny:true]; PillButton *button = [[PillButton alloc] initAsPrimaryButton:false makeTiny:true];
[button styleSecondary];
[button setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal]; [button setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
[button setContentHuggingPriority:800 forAxis:UILayoutConstraintAxisHorizontal]; [button setContentHuggingPriority:800 forAxis:UILayoutConstraintAxisHorizontal];