Merge branch 'develop' into feature/revised_accessibility

This commit is contained in:
Kevin G Christiano 2020-05-07 16:38:31 -04:00
commit 3abe936d7d
13 changed files with 418 additions and 125 deletions

View File

@ -2103,7 +2103,7 @@
0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */, 0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */,
011B58F023A2AA980085F53C /* ListItemModelProtocol.swift in Sources */, 011B58F023A2AA980085F53C /* ListItemModelProtocol.swift in Sources */,
D22479962316AF6E003FCCF9 /* HeadlineBodyLink.swift in Sources */, D22479962316AF6E003FCCF9 /* HeadlineBodyLink.swift in Sources */,
8DE5BECD2456F7A200772E76 /* ListTwoColumnDropdownSelectorsModel.swift in Sources */, 8DE5BECD2456F7A200772E76 /* ListTwoColumnDropdownSelectorsModel.swift in Sources */,
0A41BA7F23453A6400D4C0BC /* TextEntryField.swift in Sources */, 0A41BA7F23453A6400D4C0BC /* TextEntryField.swift in Sources */,
BB55B51D244482C1002001AD /* ListRightVariablePriceChangeBodyText.swift in Sources */, BB55B51D244482C1002001AD /* ListRightVariablePriceChangeBodyText.swift in Sources */,
017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */, 017BEB382360C6AC0024EF95 /* RadioButtonLabel.swift in Sources */,

View File

@ -8,40 +8,75 @@
import UIKit import UIKit
public enum ButtonStyle: String, Codable { public typealias FacadeElements = (fill: UIColor?, text: UIColor?, border: UIColor?)
case primary
case secondary
}
public enum ButtonSize: String, Codable {
case standard
case tiny
}
public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol { public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupWatcherFieldProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "button" public static var identifier: String = "button"
public var backgroundColor: Color? public var backgroundColor: Color?
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 style: ButtonStyle? public var style: Styler.Button.Style? {
public var size: ButtonSize? = .standard didSet {
public var fillColor: Color? guard let style = style else { return }
public var textColor: Color? switch style {
public var borderColor: Color? 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 disabledFillColor: Color?
public var disabledTextColor: Color? public var disabledTextColor: Color?
public var disabledBorderColor: 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) { public func setValidity(_ valid: Bool, group: FormGroupRule) {
enabled = valid enabled = valid
updateUI?() updateUI?()
} }
/// Temporary binding mechanism for the view to update on enable changes. /// 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) { public init(with title: String, action: ActionModelProtocol) {
self.title = title self.title = title
self.action = action self.action = action
@ -52,71 +87,173 @@ public class ButtonModel: ButtonModelProtocol, MoleculeModelProtocol, FormGroupW
self.action = action self.action = action
style = .secondary style = .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
} }
//--------------------------------------------------
// 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 { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case title case title
case inverted
case action case action
case enabled case enabled
case style case style
case size case size
case groupName
case fillColor case fillColor
case textColor case textColor
case borderColor case borderColor
case disabledFillColor case disabledFillColor
case disabledTextColor case disabledTextColor
case disabledBorderColor case disabledBorderColor
case groupName
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
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) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
title = try typeContainer.decode(String.self, forKey: .title) title = try typeContainer.decode(String.self, forKey: .title)
action = try typeContainer.decodeModel(codingKey: .action) 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(Styler.Button.Style.self, forKey: .style) {
}
if let style = try typeContainer.decodeIfPresent(ButtonStyle.self, forKey: .style) {
self.style = 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 self.size = size
} }
if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) {
self.enabled = enabled self.enabled = enabled
} }
fillColor = try typeContainer.decodeIfPresent(Color.self, forKey: .fillColor)
textColor = try typeContainer.decodeIfPresent(Color.self, forKey: .textColor) if let inverted = try typeContainer.decodeIfPresent(Bool.self, forKey: .inverted) {
borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) self.inverted = inverted
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 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 { public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)
try container.encode(title, forKey: .title) 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.encode(enabled, forKey: .enabled)
try container.encodeIfPresent(style, forKey: .style) try container.encode(inverted, forKey: .inverted)
try container.encodeIfPresent(size, forKey: .size) try container.encodeModel(action, forKey: .action)
try container.encodeIfPresent(fillColor, forKey: .fillColor) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(textColor, forKey: .textColor) try container.encodeIfPresent(enabledFillColor, forKey: .fillColor)
try container.encodeIfPresent(borderColor, forKey: .borderColor) try container.encodeIfPresent(enabledTextColor, forKey: .textColor)
try container.encodeIfPresent(enabledBorderColor, forKey: .borderColor)
try container.encodeIfPresent(disabledFillColor, forKey: .disabledFillColor) try container.encodeIfPresent(disabledFillColor, forKey: .disabledFillColor)
try container.encodeIfPresent(disabledTextColor, forKey: .disabledTextColor) try container.encodeIfPresent(disabledTextColor, forKey: .disabledTextColor)
try container.encodeIfPresent(disabledBorderColor, forKey: .disabledBorderColor) try container.encodeIfPresent(disabledBorderColor, forKey: .disabledBorderColor)
try container.encodeIfPresent(style, forKey: .style)
try container.encodeIfPresent(size, forKey: .size)
try container.encodeIfPresent(groupName, forKey: .groupName) try container.encodeIfPresent(groupName, forKey: .groupName)
} }
} }

View File

@ -8,80 +8,105 @@
import UIKit import UIKit
open class PillButton: Button, MVMCoreUIViewConstrainingProtocol { open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
// Used to size the button. //--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
/// Used to size the button.
var size = MVMCoreUIUtility.getWidth() var size = MVMCoreUIUtility.getWidth()
var buttonModel: ButtonModel? { var buttonModel: ButtonModel? {
get { return model as? ButtonModel } get { return model as? ButtonModel }
} }
// Need to re-style on set. /// Need to re-style on set.
open override var isEnabled: Bool { open override var isEnabled: Bool {
didSet { didSet { style() }
style()
}
} }
private enum ButtonHeight: CGFloat { //--------------------------------------------------
case tiny = 20 // MARK: - Computed Properties
case standard = 42 //--------------------------------------------------
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 /// The primary styling for a button. Should be used for main buttons
public func stylePrimary() { public func stylePrimary() {
setTitleColor(.white, for: .normal)
setTitleColor(.white, for: .disabled) enabledTitleColor = buttonModel?.enabledColors.text ?? .mvmWhite
disabledTitleColor = buttonModel?.disabledColors.text ?? .mvmWhite
layer.borderWidth = 0 layer.borderWidth = 0
if isEnabled { backgroundColor = isEnabled ? buttonModel?.enabledColors.fill ?? .mvmBlack : buttonModel?.disabledColors.fill ?? .mvmCoolGray6
backgroundColor = .black
} else {
backgroundColor = .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() {
setTitleColor(.black, for: .normal)
setTitleColor(.mvmCoolGray6, for: .disabled) enabledTitleColor = buttonModel?.enabledColors.text ?? .mvmBlack
disabledTitleColor = buttonModel?.disabledColors.text ?? .mvmCoolGray6
backgroundColor = .clear backgroundColor = .clear
layer.borderWidth = 1 layer.borderWidth = 1
if isEnabled { borderColor = isEnabled ? buttonModel?.enabledColors.border ?? .mvmBlack : buttonModel?.disabledColors.border ?? .mvmCoolGray6
layer.borderColor = UIColor.black.cgColor
} else {
layer.borderColor = UIColor.mvmCoolGray6.cgColor
}
} }
/// Styles the button based on the model style /// Styles the button based on the model style
private func style() { private func style() {
switch buttonModel?.style { switch buttonModel?.style {
case .secondary: case .secondary:
styleSecondary() styleSecondary()
default: default:
stylePrimary() 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 isEnabled {
if let fillColor = buttonModel?.fillColor { if let fillColor = buttonModel?.enabledColors.fill {
backgroundColor = fillColor.uiColor backgroundColor = fillColor
} }
if let borderColor = buttonModel?.borderColor {
if let borderColor = buttonModel?.enabledColors.border {
layer.borderWidth = 1 layer.borderWidth = 1
layer.borderColor = borderColor.cgColor self.borderColor = borderColor
} }
} else { } else {
if let fillColor = buttonModel?.disabledFillColor { if let fillColor = buttonModel?.disabledColors.fill {
backgroundColor = fillColor.uiColor backgroundColor = fillColor
} }
if let borderColor = buttonModel?.disabledBorderColor {
if let borderColor = buttonModel?.disabledColors.border {
layer.borderWidth = 1 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) 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 { switch buttonSize {
case .tiny: 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: 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 { private func getMinimumWidth() -> CGFloat {
switch buttonModel?.size { switch buttonModel?.size {
case .tiny: case .tiny:
return MFSizeObject(standardSize: 49.0, standardiPadPortraitSize: 90.0, iPadProLandscapeSize: 135.0)?.getValueBased(onSize: size) ?? 49.0 return MFSizeObject(standardSize: 49,
default: standardiPadPortraitSize: 90,
return 151.0 iPadProLandscapeSize: 135)?.getValueBased(onSize: size) ?? 49
default: return 151
} }
} }
open override var intrinsicContentSize: CGSize { open override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize let size = super.intrinsicContentSize
let width = size.width + (2 * getInnerPadding()) let width = size.width + (2 * getInnerPadding())
return CGSize(width: max(width, getMinimumWidth()), height: getHeight()) return CGSize(width: max(width, getMinimumWidth()), height: getHeight())
} }
// MARK: - MoleculeViewProtocol //--------------------------------------------------
// MARK: - MVMCoreViewProtocol
//--------------------------------------------------
open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
// The button will get styled in the enable check in super. // The button will get styled in the enable check in super.
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)
model.updateUI = { [weak self] in model.updateUI = { [weak self] in
MVMCoreDispatchUtility.performBlock(onMainThread: { MVMCoreDispatchUtility.performBlock(onMainThread: {
self?.enableField(model.enabled) self?.enableField(model.enabled)
@ -134,39 +174,46 @@ open class PillButton: Button, MVMCoreUIViewConstrainingProtocol {
FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate) FormValidator.setupValidation(for: model, delegate: delegateObject?.formHolderDelegate)
} }
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()) PillButton.getHeight(for: (model as? ButtonModel)?.size, size: MVMCoreUIUtility.getWidth())
} }
// MARK: - MVMCoreViewProtocol
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() invalidateIntrinsicContentSize()
switch buttonModel?.size { switch buttonModel?.size {
case .tiny: 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: 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() layer.cornerRadius = getInnerPadding()
} }
open override func setupView() { open override func setupView() {
super.setupView() super.setupView()
titleLabel?.numberOfLines = 1 titleLabel?.numberOfLines = 1
titleLabel?.lineBreakMode = .byTruncatingTail titleLabel?.lineBreakMode = .byTruncatingTail
titleLabel?.textAlignment = .center titleLabel?.textAlignment = .center
contentHorizontalAlignment = .center contentHorizontalAlignment = .center
stylePrimary() stylePrimary()
} }
//--------------------------------------------------
// MARK: - MVMCoreUIViewConstrainingProtocol // MARK: - MVMCoreUIViewConstrainingProtocol
//--------------------------------------------------
open func horizontalAlignment() -> UIStackView.Alignment { open func horizontalAlignment() -> UIStackView.Alignment {
return .center return .center
} }
public func enableField(_ enable: Bool) { public func enableField(_ enable: Bool) {
isEnabled = enable isEnabled = enable
} }

View File

@ -22,7 +22,7 @@ public class ListRightVariableButtonAllTextAndLinksModel: ListItemModel, Molecul
override public func setDefaults() { override public func setDefaults() {
super.setDefaults() super.setDefaults()
self.button.size = .tiny self.button.size = .tiny
self.button.style = ButtonStyle.secondary self.button.style = .secondary
} }
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {

View File

@ -9,11 +9,24 @@
import UIKit import UIKit
@objcMembers open class TwoButtonView: View, MVMCoreUIViewConstrainingProtocol { @objcMembers open class TwoButtonView: View, MVMCoreUIViewConstrainingProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
open var primaryButton: PillButton = PillButton() open var primaryButton: PillButton = PillButton()
open var secondaryButton: PillButton = PillButton() open var secondaryButton: PillButton = PillButton()
private var stack = UIStackView() private var stack = UIStackView()
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
private var equalWidthConstraint: NSLayoutConstraint? private var equalWidthConstraint: NSLayoutConstraint?
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
public init() { public init() {
super.init(frame: .zero) super.init(frame: .zero)
} }
@ -26,16 +39,21 @@ import UIKit
super.init(frame: frame) super.init(frame: frame)
} }
public func setDefault() { //--------------------------------------------------
// MARK: - Lifecycle
//--------------------------------------------------
public func setDefaultAppearance() {
primaryButton.stylePrimary() primaryButton.stylePrimary()
secondaryButton.styleSecondary() secondaryButton.styleSecondary()
} }
// MARK: - MVMCoreViewProtocol
open override func updateView(_ size: CGFloat) { open override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
self.primaryButton.updateView(size)
self.secondaryButton.updateView(size) primaryButton.updateView(size)
secondaryButton.updateView(size)
} }
open override func setupView() { open override func setupView() {
@ -52,58 +70,70 @@ import UIKit
equalWidthConstraint?.isActive = true equalWidthConstraint?.isActive = true
} }
//--------------------------------------------------
// MARK: - Stack Manipulation // MARK: - Stack Manipulation
//--------------------------------------------------
public func showPrimaryButton() { public func showPrimaryButton() {
if !stack.arrangedSubviews.contains(primaryButton) { if !stack.arrangedSubviews.contains(primaryButton) {
stack.addArrangedSubview(primaryButton) stack.addArrangedSubview(primaryButton)
primaryButton.isHidden = false primaryButton.isHidden = false
} }
if secondaryButton.superview != nil { if secondaryButton.superview != nil {
equalWidthConstraint?.isActive = true equalWidthConstraint?.isActive = true
} }
} }
public func showSecondaryButton() { public func showSecondaryButton() {
if !stack.arrangedSubviews.contains(secondaryButton) { if !stack.arrangedSubviews.contains(secondaryButton) {
stack.insertArrangedSubview(secondaryButton, at: 0) stack.insertArrangedSubview(secondaryButton, at: 0)
secondaryButton.isHidden = false secondaryButton.isHidden = false
} }
if primaryButton.superview != nil { if primaryButton.superview != nil {
equalWidthConstraint?.isActive = true equalWidthConstraint?.isActive = true
} }
} }
public func hidePrimaryButton() { public func hidePrimaryButton() {
if primaryButton.superview != nil { if primaryButton.superview != nil {
stack.removeArrangedSubview(primaryButton) stack.removeArrangedSubview(primaryButton)
primaryButton.isHidden = true primaryButton.isHidden = true
} }
equalWidthConstraint?.isActive = false equalWidthConstraint?.isActive = false
} }
public func hideSecondaryButton() { public func hideSecondaryButton() {
if secondaryButton.superview != nil { if secondaryButton.superview != nil {
stack.removeArrangedSubview(secondaryButton) stack.removeArrangedSubview(secondaryButton)
secondaryButton.isHidden = true secondaryButton.isHidden = true
} }
equalWidthConstraint?.isActive = false equalWidthConstraint?.isActive = false
} }
//--------------------------------------------------
// MARK: - MoleculeViewProtocol // MARK: - MoleculeViewProtocol
//--------------------------------------------------
open override func reset() { open override func reset() {
super.reset() super.reset()
setDefault()
} setDefaultAppearance()
// MARK: - MVMCoreUIViewConstrainingProtocol
open func horizontalAlignment() -> UIStackView.Alignment {
return .center
} }
// MARK: - MoleculeViewProtocol
public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { public override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
guard let model = model as? TwoButtonViewModel, 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) return PillButton.estimatedHeight(with: buttonModel, delegateObject)
} }
@ -118,6 +148,7 @@ import UIKit
} else { } else {
hideSecondaryButton() hideSecondaryButton()
} }
if let primaryModel = model.primaryButton { if let primaryModel = model.primaryButton {
showPrimaryButton() showPrimaryButton()
primaryButton.set(with: primaryModel, delegateObject, additionalData) primaryButton.set(with: primaryModel, delegateObject, additionalData)
@ -125,4 +156,11 @@ import UIKit
hidePrimaryButton() hidePrimaryButton()
} }
} }
//--------------------------------------------------
// MARK: - MVMCoreUIViewConstrainingProtocol
//--------------------------------------------------
open func horizontalAlignment() -> UIStackView.Alignment {
return .center
}
} }

View File

@ -8,35 +8,54 @@
import UIKit import UIKit
public class TwoButtonViewModel: MoleculeModelProtocol { public class TwoButtonViewModel: MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "twoButtonView" public static var identifier: String = "twoButtonView"
public var backgroundColor: Color? public var backgroundColor: Color?
public var primaryButton: ButtonModel? public var primaryButton: ButtonModel?
public var secondaryButton: ButtonModel? public var secondaryButton: ButtonModel?
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case moleculeName case moleculeName
case backgroundColor case backgroundColor
case primaryButton case primaryButton
case secondaryButton case secondaryButton
} }
//--------------------------------------------------
// MARK: - Initialzer
//--------------------------------------------------
public init(_ primaryButton: ButtonModel?, _ secondaryButton: ButtonModel?) { public init(_ primaryButton: ButtonModel?, _ secondaryButton: ButtonModel?) {
self.primaryButton = primaryButton self.primaryButton = primaryButton
self.secondaryButton = secondaryButton self.secondaryButton = secondaryButton
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
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) backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
primaryButton = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .primaryButton) primaryButton = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .primaryButton)
if primaryButton?.style == nil {
primaryButton?.style = .primary
}
secondaryButton = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .secondaryButton) secondaryButton = try typeContainer.decodeIfPresent(ButtonModel.self, forKey: .secondaryButton)
// Default value
if secondaryButton?.style == nil { if secondaryButton?.style == nil {
secondaryButton?.style = .secondary secondaryButton?.style = .secondary
} }
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(moleculeName, forKey: .moleculeName) try container.encode(moleculeName, forKey: .moleculeName)

View File

@ -8,24 +8,39 @@
import Foundation import Foundation
@objcMembers public class TemplateModel: MVMControllerModelProtocol { @objcMembers public class TemplateModel: MVMControllerModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public class var identifier: String { public class var identifier: String {
return "" return ""
} }
public var pageType: String public var pageType: String
public var template: String { public var template: String {
// Although this is done in the extension, it is needed for the encoding. // Although this is done in the extension, it is needed for the encoding.
return Self.identifier return Self.identifier
} }
public var backgroundColor: Color? public var backgroundColor: Color?
public var screenHeading: String? public var screenHeading: String?
public var navigationItem: (NavigationItemModelProtocol & MoleculeModelProtocol)? public var navigationItem: (NavigationItemModelProtocol & MoleculeModelProtocol)?
public var formRules: [FormGroupRule]? public var formRules: [FormGroupRule]?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(pageType: String) { public init(pageType: String) {
self.pageType = pageType self.pageType = pageType
} }
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case pageType case pageType
case template case template
@ -34,6 +49,10 @@ import Foundation
case formRules case formRules
case navigationItem case navigationItem
} }
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
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)

View File

@ -24,25 +24,25 @@ import UIKit
open override func viewForTop() -> UIView? { open override func viewForTop() -> UIView? {
guard let headerModel = templateModel?.header, guard let headerModel = templateModel?.header,
let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar) else { let molecule = MoleculeObjectMapping.shared()?.createMolecule(headerModel, delegateObject: delegateObjectIVar)
return nil else { return nil }
}
return molecule return molecule
} }
open override func viewForMiddle() -> UIView? { open override func viewForMiddle() -> UIView? {
guard let middleModel = templateModel?.middle, guard let middleModel = templateModel?.middle,
let molecule = MoleculeObjectMapping.shared()?.createMolecule(middleModel, delegateObject: delegateObjectIVar) else { let molecule = MoleculeObjectMapping.shared()?.createMolecule(middleModel, delegateObject: delegateObjectIVar)
return nil else { return nil }
}
return molecule return molecule
} }
override open func viewForBottom() -> UIView? { override open func viewForBottom() -> UIView? {
guard let footerModel = templateModel?.footer, guard let footerModel = templateModel?.footer,
let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar) else { let molecule = MoleculeObjectMapping.shared()?.createMolecule(footerModel, delegateObject: delegateObjectIVar)
return nil else { return nil }
}
return molecule return molecule
} }

View File

@ -81,6 +81,7 @@ open class TextViewModel: TextEntryFieldModel {
} }
public override func encode(to encoder: Encoder) throws { public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText) try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
try container.encodeIfPresent(height, forKey: .height) try container.encodeIfPresent(height, forKey: .height)

View File

@ -258,7 +258,7 @@ import UIKit
viewRespectsSystemMinimumLayoutMargins = false viewRespectsSystemMinimumLayoutMargins = false
// Presents from the bottom. // Presents from the bottom.
modalPresentationStyle = MVMCoreGetterUtility.isOnIPad() ? UIModalPresentationStyle.formSheet : UIModalPresentationStyle.overCurrentContext modalPresentationStyle = MVMCoreGetterUtility.isOnIPad() ? .formSheet : .overCurrentContext
// Create the default delegate object. // Create the default delegate object.
delegateObjectIVar = MVMCoreUIDelegateObject.create(withDelegateForAll: self) delegateObjectIVar = MVMCoreUIDelegateObject.create(withDelegateForAll: self)
@ -339,18 +339,18 @@ import UIKit
} }
// MARK: - MVMCoreActionDelegateProtocol // 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) formValidator?.addFormParams(requestParameters: requestParameters)
requestParameters.parentPageType = loadObject?.pageJSON?.optionalStringForKey("parentPageType") requestParameters.parentPageType = loadObject?.pageJSON?.optionalStringForKey("parentPageType")
MVMCoreActionHandler.defaultHandleOpenPage(for: requestParameters, additionalData: additionalData, delegateObject: delegateObject()) 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) MVMCoreUILoggingHandler.shared()?.defaultLogAction(forController: self, actionInformation: actionInformation, additionalData: additionalData)
} }
// MARK: - MoleculeDelegateProtocol // MARK: - MoleculeDelegateProtocol
open func getModuleWithName(_ name: String?) -> [AnyHashable : Any]? { open func getModuleWithName(_ name: String?) -> [AnyHashable: Any]? {
guard let name = name else { return nil } guard let name = name else { return nil }
return loadObject?.modulesJSON?.optionalDictionaryForKey(name) return loadObject?.modulesJSON?.optionalDictionaryForKey(name)
} }
@ -391,7 +391,7 @@ import UIKit
view.accessibilityElements = [pickerView, toolBar] view.accessibilityElements = [pickerView, toolBar]
} }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { 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 textField === selectedField {
if UIAccessibility.isVoiceOverRunning { if UIAccessibility.isVoiceOverRunning {
view.accessibilityElements = nil view.accessibilityElements = nil
UIAccessibility.post(notification: UIAccessibility.Notification.layoutChanged, argument: textField) UIAccessibility.post(notification: .layoutChanged, argument: textField)
} }
selectedField = nil selectedField = nil
} }

View File

@ -9,8 +9,10 @@
import UIKit import UIKit
open class Container: View, ContainerProtocol { open class Container: View, ContainerProtocol {
public var view: UIView? public var view: UIView?
let containerHelper = ContainerHelper() let containerHelper = ContainerHelper()
var containerModel: ContainerModelProtocol? { var containerModel: ContainerModelProtocol? {
get { return model as? ContainerModelProtocol } get { return model as? ContainerModelProtocol }
} }

View File

@ -55,6 +55,13 @@ public final class Color: Codable {
determineRGBA() determineRGBA()
} }
public init?(uiColor: UIColor?) {
guard let uiColor = uiColor else { return nil }
self.uiColor = uiColor
hex = UIColor.hexString(for: uiColor) ?? ""
determineRGBA()
}
init?(name: String) { init?(name: String) {
guard let colorTuple = UIColor.names[name] else { return nil } guard let colorTuple = UIColor.names[name] else { return nil }
self.uiColor = colorTuple.uiColor self.uiColor = colorTuple.uiColor

View File

@ -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 // MARK: - Functions
//-------------------------------------------------- //--------------------------------------------------