Merge branch 'feature/coding' into 'develop'
Feature/coding See merge request BPHV_MIPS/mvm_core_ui!197
This commit is contained in:
commit
fddd6640e4
File diff suppressed because it is too large
Load Diff
75
MVMCoreUI/Atoms/Buttons/ButtonModel.swift
Normal file
75
MVMCoreUI/Atoms/Buttons/ButtonModel.swift
Normal file
@ -0,0 +1,75 @@
|
||||
//
|
||||
// ButtonModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 1/13/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public enum ButtonStyle: String, Codable {
|
||||
case primary
|
||||
case secondary
|
||||
}
|
||||
|
||||
public enum ButtonSize: String, Codable {
|
||||
case standard
|
||||
case tiny
|
||||
}
|
||||
|
||||
public class ButtonModel: MoleculeModelProtocol {
|
||||
public static var identifier: String = "button"
|
||||
public var moleculeName: String?
|
||||
public var backgroundColor: Color?
|
||||
public var title: String
|
||||
public var action: ActionModelProtocol
|
||||
public var style: ButtonStyle?
|
||||
public var size: ButtonSize? = .standard
|
||||
public var required: Bool?
|
||||
public var requiredGroups: [String]?
|
||||
|
||||
init(with title: String, action: ActionModelProtocol) {
|
||||
self.title = title
|
||||
self.action = action
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case backgroundColor
|
||||
case title
|
||||
case action
|
||||
case style
|
||||
case size
|
||||
case required
|
||||
case requiredGroups
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
moleculeName = try typeContainer.decodeIfPresent(String.self, forKey: .moleculeName)
|
||||
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
title = try typeContainer.decode(String.self, forKey: .title)
|
||||
action = try typeContainer.decodeModel(codingKey: .action, typeCodingKey: ActionCodingKey.actionType)
|
||||
required = try typeContainer.decodeIfPresent(Bool.self, forKey: .required)
|
||||
requiredGroups = try typeContainer.decodeIfPresent([String].self, forKey: .requiredGroups)
|
||||
if let style = try typeContainer.decodeIfPresent(ButtonStyle.self, forKey: .style) {
|
||||
self.style = style
|
||||
}
|
||||
if let size = try typeContainer.decodeIfPresent(ButtonSize.self, forKey: .size) {
|
||||
self.size = size
|
||||
}
|
||||
}
|
||||
|
||||
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.encodeIfPresent(style, forKey: .style)
|
||||
try container.encodeIfPresent(size, forKey: .size)
|
||||
try container.encodeIfPresent(required, forKey: .required)
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,8 @@
|
||||
//
|
||||
|
||||
|
||||
open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol {
|
||||
open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, ModelMoleculeViewProtocol {
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - Constants
|
||||
//------------------------------------------------------
|
||||
@ -88,6 +89,8 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI
|
||||
|
||||
NSLayoutConstraint(item: caretView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0).isActive = true
|
||||
NSLayoutConstraint(item: self, attribute: .right, relatedBy: .greaterThanOrEqual, toItem: caretView, attribute: .right, multiplier: 1.0, constant: 0).isActive = true
|
||||
caretView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true
|
||||
bottomAnchor.constraint(greaterThanOrEqualTo: caretView.bottomAnchor).isActive = true
|
||||
contentHorizontalAlignment = .left
|
||||
|
||||
//set correct color after layout
|
||||
@ -107,6 +110,7 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI
|
||||
@objc open func setAsMolecule() {
|
||||
|
||||
backgroundColor = .clear
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
setTitleColor(enabledColor, for: .normal)
|
||||
setTitleColor(disabledColor, for: .disabled)
|
||||
}
|
||||
@ -137,6 +141,20 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI
|
||||
}
|
||||
}
|
||||
|
||||
public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
guard let caretLinkModel = model as? CaretLinkModel else { return }
|
||||
if let color = caretLinkModel.backgroundColor {
|
||||
backgroundColor = color.uiColor
|
||||
}
|
||||
enabledColor = caretLinkModel.enabledColor.uiColor
|
||||
if let color = caretLinkModel.disabledColor {
|
||||
disabledColor = color.uiColor
|
||||
}
|
||||
isEnabled = caretLinkModel.enabled
|
||||
set(with: caretLinkModel.action, delegateObject: delegateObject, additionalData: additionalData)
|
||||
setTitle(caretLinkModel.title, for: .normal)
|
||||
}
|
||||
|
||||
public func needsToBeConstrained() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
60
MVMCoreUI/Atoms/Buttons/CaretLinkModel.swift
Normal file
60
MVMCoreUI/Atoms/Buttons/CaretLinkModel.swift
Normal file
@ -0,0 +1,60 @@
|
||||
//
|
||||
// CaretLinkModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Suresh, Kamlesh on 12/13/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MVMCore
|
||||
|
||||
public class CaretLinkModel: MoleculeModelProtocol {
|
||||
public static var identifier: String = "caretLink"
|
||||
public var backgroundColor: Color?
|
||||
public var title: String
|
||||
public var action: ActionModelProtocol
|
||||
public var enabledColor: Color = Color(uiColor: .black)
|
||||
public var disabledColor: Color? = Color(uiColor: .mfSilver())
|
||||
public var enabled: Bool = true
|
||||
|
||||
public init(title: String, action: ActionModelProtocol) {
|
||||
self.title = title
|
||||
self.action = action
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case backgroundColor
|
||||
case title
|
||||
case action
|
||||
case enabledColor
|
||||
case disabledColor
|
||||
case enabled
|
||||
}
|
||||
|
||||
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)
|
||||
if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledColor) {
|
||||
enabledColor = color
|
||||
}
|
||||
if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledColor) {
|
||||
disabledColor = color
|
||||
}
|
||||
if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) {
|
||||
self.enabled = enabled
|
||||
}
|
||||
action = try typeContainer.decodeModel(codingKey: .action, typeCodingKey: ActionCodingKey.actionType)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(title, forKey: .title)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeModel(action, forKey: .action)
|
||||
try container.encode(enabled, forKey: .enabledColor)
|
||||
try container.encodeIfPresent(disabledColor, forKey: .disabledColor)
|
||||
try container.encode(enabled, forKey: .enabled)
|
||||
}
|
||||
}
|
||||
88
MVMCoreUI/Atoms/Buttons/Link.swift
Normal file
88
MVMCoreUI/Atoms/Buttons/Link.swift
Normal file
@ -0,0 +1,88 @@
|
||||
//
|
||||
// Link.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Robinson, Blake on 11/26/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
@objcMembers open class Link: Button {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Lifecycle
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func draw(_ rect: CGRect) {
|
||||
|
||||
guard let textRect = titleLabel?.frame else { return }
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()
|
||||
|
||||
// Set line to the same color as the text
|
||||
if let color = titleLabel?.textColor?.cgColor {
|
||||
context?.setStrokeColor(color)
|
||||
}
|
||||
|
||||
// x should be according to the text, not the button
|
||||
let x = textRect.origin.x
|
||||
|
||||
// Line is 1 point below the text
|
||||
let y = textRect.origin.y + textRect.size.height + 1
|
||||
|
||||
context?.move(to: CGPoint(x: x, y: y))
|
||||
context?.addLine(to: CGPoint(x: x + textRect.size.width, y: y))
|
||||
context?.strokePath()
|
||||
}
|
||||
|
||||
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
guard let model = model as? LinkModel else { return }
|
||||
setTitle(model.title, for: .normal)
|
||||
setTitleColor(model.textColor.uiColor, for: .normal)
|
||||
setTitleColor(model.disabledColor.uiColor, for: .disabled)
|
||||
isEnabled = model.enabled
|
||||
set(with: model.action, delegateObject: delegateObject, additionalData: additionalData)
|
||||
}
|
||||
|
||||
public static func estimatedHeight(forRow molecule: ModuleMoleculeModel?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return 31.0
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreViewProtocol
|
||||
extension Link {
|
||||
|
||||
public override func updateView(_ size: CGFloat) {
|
||||
super.updateView(size)
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
var width = size
|
||||
if MVMCoreGetterUtility.fequal(a: Float(CGFloat.leastNormalMagnitude), b: Float(size)) {
|
||||
width = MVMCoreUIUtility.getWidth()
|
||||
}
|
||||
self.titleLabel?.font = MFStyler.fontB2(forWidth: width)
|
||||
}
|
||||
}
|
||||
|
||||
public override func setupView() {
|
||||
super.setupView()
|
||||
backgroundColor = .clear
|
||||
contentMode = .redraw
|
||||
setTitleColor(.mvmBlack, for: .normal)
|
||||
setTitleColor(.mvmCoolGray6, for: .disabled)
|
||||
titleLabel?.numberOfLines = 1
|
||||
titleLabel?.lineBreakMode = .byTruncatingTail
|
||||
titleLabel?.textAlignment = .left
|
||||
contentHorizontalAlignment = .left
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIViewConstrainingProtocol
|
||||
extension Link: MVMCoreUIViewConstrainingProtocol {
|
||||
|
||||
public func horizontalAlignment() -> UIStackView.Alignment {
|
||||
return .leading
|
||||
}
|
||||
}
|
||||
60
MVMCoreUI/Atoms/Buttons/LinkModel.swift
Normal file
60
MVMCoreUI/Atoms/Buttons/LinkModel.swift
Normal file
@ -0,0 +1,60 @@
|
||||
//
|
||||
// LinkModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 1/13/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public class LinkModel: MoleculeModelProtocol {
|
||||
public static var identifier: String = "link"
|
||||
public var backgroundColor: Color?
|
||||
public var title: String
|
||||
public var action: ActionModelProtocol
|
||||
public var enabled = true
|
||||
public var textColor = Color(uiColor: .mvmBlack)
|
||||
public var disabledColor = Color(uiColor: .mvmCoolGray6)
|
||||
|
||||
public init(title: String, action: ActionModelProtocol) {
|
||||
self.title = title
|
||||
self.action = action
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case backgroundColor
|
||||
case title
|
||||
case action
|
||||
case enabled
|
||||
case textColor
|
||||
case disabledColor
|
||||
}
|
||||
|
||||
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, typeCodingKey: ActionCodingKey.actionType)
|
||||
if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) {
|
||||
self.enabled = enabled
|
||||
}
|
||||
if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .textColor) {
|
||||
textColor = color
|
||||
}
|
||||
|
||||
if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledColor) {
|
||||
disabledColor = color
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
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(textColor, forKey: .textColor)
|
||||
try container.encode(disabledColor, forKey: .disabledColor)
|
||||
}
|
||||
}
|
||||
23
MVMCoreUI/Atoms/Buttons/MFCustomButton+ActionModel.swift
Normal file
23
MVMCoreUI/Atoms/Buttons/MFCustomButton+ActionModel.swift
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// MFCustomButton+ActionModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 1/9/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension MFCustomButton {
|
||||
func set(with action: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
buttonDelegate = delegateObject?.buttonDelegate
|
||||
add({ [weak self] sender in
|
||||
guard let self = self else { return }
|
||||
if let data = try? action.encode(using: JSONEncoder()),
|
||||
let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any],
|
||||
delegateObject?.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
}, for: .touchUpInside)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
//
|
||||
// PrimaryButton+MoleculeProtocolExtension.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 1/13/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// temporary until link is finished
|
||||
extension PrimaryButton: ModelMoleculeViewProtocol {
|
||||
public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
|
||||
guard let model = model as? ButtonModel else { return }
|
||||
setTitle(model.title, for: .normal)
|
||||
backgroundColor = model.backgroundColor?.uiColor
|
||||
|
||||
self.validationRequired = model.required ?? false
|
||||
self.requiredGroupsList = model.requiredGroups
|
||||
|
||||
if self.validationRequired,
|
||||
let selfForm = self as? FormValidationEnableDisableProtocol {
|
||||
FormValidator.setupValidation(molecule: selfForm, delegate: delegateObject?.formValidationProtocol)
|
||||
}
|
||||
|
||||
|
||||
if let style = model.style {
|
||||
switch style {
|
||||
case .primary:
|
||||
setAsStandardCustom()
|
||||
case .secondary:
|
||||
setAsSecondaryCustom()
|
||||
}
|
||||
}
|
||||
if let size = model.size {
|
||||
switch size {
|
||||
case .standard:
|
||||
setAsTiny(false)
|
||||
case .tiny:
|
||||
setAsTiny(true)
|
||||
}
|
||||
}
|
||||
set(with: model.action, delegateObject: delegateObject, additionalData: additionalData)
|
||||
}
|
||||
}
|
||||
@ -68,6 +68,14 @@ import UIKit
|
||||
container.trailingAnchor.constraint(equalTo: dropDownCaretView.trailingAnchor, constant: 16).isActive = true
|
||||
dropDownCaretView.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
|
||||
}
|
||||
|
||||
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
|
||||
guard let model = model as? BaseDropdownEntryFieldModel else { return }
|
||||
|
||||
dropDownCaretView.setWithModel(model.caretView, delegateObject, additionalData)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
|
||||
45
MVMCoreUI/Atoms/TextFields/BaseDropdownEntryFieldModel.swift
Normal file
45
MVMCoreUI/Atoms/TextFields/BaseDropdownEntryFieldModel.swift
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// BaseDropdownEntryFieldModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 1/22/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
@objcMembers public class BaseDropdownEntryFieldModel: TextEntryFieldModel {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public var caretView: CaretViewModel?
|
||||
|
||||
public override class var identifier: String {
|
||||
return ""
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case caretView
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
caretView = try typeContainer.decodeIfPresent(CaretViewModel.self, forKey: .caretView)
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(caretView, forKey: .caretView)
|
||||
}
|
||||
}
|
||||
@ -22,7 +22,20 @@ import UIKit
|
||||
return calendar
|
||||
}()
|
||||
|
||||
public var dateFormat: String?
|
||||
public var dateFormatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .medium
|
||||
formatter.timeZone = NSTimeZone.system
|
||||
formatter.locale = .current
|
||||
formatter.formatterBehavior = .default
|
||||
return formatter
|
||||
}()
|
||||
|
||||
public var dateFormat: String = "MMM d, y" {
|
||||
didSet {
|
||||
dateFormatter.dateFormat = dateFormat
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
@ -83,7 +96,7 @@ import UIKit
|
||||
if calendar.isDate(date, inSameDayAs: Date()) {
|
||||
text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string")
|
||||
} else {
|
||||
text = dateFormatter().string(from: date)
|
||||
text = dateFormatter.string(from: date)
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,19 +111,12 @@ import UIKit
|
||||
setTextWith(date: datePicker?.date)
|
||||
}
|
||||
|
||||
public func dateFormatter() -> DateFormatter {
|
||||
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .medium
|
||||
formatter.timeZone = NSTimeZone.system
|
||||
formatter.locale = .current
|
||||
formatter.formatterBehavior = .default
|
||||
guard let model = model as? DateDropdownEntryFieldModel else { return }
|
||||
|
||||
if let dateFormat = dateFormat {
|
||||
formatter.dateFormat = dateFormat
|
||||
}
|
||||
|
||||
return formatter
|
||||
dateFormat = model.dateFormat
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
45
MVMCoreUI/Atoms/TextFields/DateDropdownEntryFieldModel.swift
Normal file
45
MVMCoreUI/Atoms/TextFields/DateDropdownEntryFieldModel.swift
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// DateDropdownEntryFieldModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 1/22/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
@objcMembers public class DateDropdownEntryFieldModel: BaseDropdownEntryFieldModel {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override class var identifier: String {
|
||||
return "dateDropdownEntryField"
|
||||
}
|
||||
|
||||
public var dateFormat: String = "MMM d, y"
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case dateFormat
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
dateFormat = try typeContainer.decodeIfPresent(String.self, forKey: .dateFormat) ?? "MMM d, y"
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encode(dateFormat, forKey: .dateFormat)
|
||||
}
|
||||
}
|
||||
@ -327,6 +327,22 @@ import UIKit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
|
||||
|
||||
guard let model = model as? DigitEntryFieldModel else { return }
|
||||
|
||||
numberOfDigits = model.digits
|
||||
|
||||
setAsSecureTextEntry(model.secureEntry)
|
||||
|
||||
for digitBox in digitBoxes {
|
||||
MVMCoreUICommonViewsUtility.addDismissToolbar(digitBox.digitField, delegate: delegateObject as? UITextFieldDelegate)
|
||||
}
|
||||
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TextField Delegate
|
||||
|
||||
50
MVMCoreUI/Atoms/TextFields/DigitEntryFieldModel.swift
Normal file
50
MVMCoreUI/Atoms/TextFields/DigitEntryFieldModel.swift
Normal file
@ -0,0 +1,50 @@
|
||||
//
|
||||
// DigitEntryFieldModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 1/22/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
@objcMembers public class DigitEntryFieldModel: TextEntryFieldModel {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override class var identifier: String {
|
||||
return "digitTextField"
|
||||
}
|
||||
|
||||
public var digits: Int = 4
|
||||
public var secureEntry: Bool = false
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case digits
|
||||
case secureEntry
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
digits = try typeContainer.decodeIfPresent(Int.self, forKey: .digits) ?? 4
|
||||
secureEntry = try typeContainer.decodeIfPresent(Bool.self, forKey: .secureEntry) ?? false
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encode(digits, forKey: .digits)
|
||||
try container.encode(secureEntry, forKey: .secureEntry)
|
||||
}
|
||||
}
|
||||
@ -119,6 +119,7 @@ import UIKit
|
||||
feedbackLabel.text = newFeedback
|
||||
feedbackLabel.accessibilityElementsHidden = feedbackLabel.text?.isEmpty ?? true
|
||||
entryFieldContainer.refreshUI()
|
||||
delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self)
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,6 +239,31 @@ import UIKit
|
||||
feedbackLabel.textColor = .black
|
||||
entryFieldContainer.reset()
|
||||
}
|
||||
|
||||
open override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
self.delegateObject = delegateObject
|
||||
|
||||
guard let model = model as? EntryFieldModel else { return }
|
||||
|
||||
entryFieldContainer.setWithModel(model, delegateObject, additionalData)
|
||||
|
||||
title = model.title
|
||||
feedback = model.feedback
|
||||
errorMessage = model.errorMessage
|
||||
isEnabled = model.isEnabled
|
||||
|
||||
if let isLocked = model.isLocked {
|
||||
self.isLocked = isLocked
|
||||
|
||||
} else if let isSelected = model.isSelected{
|
||||
self.isSelected = isSelected
|
||||
}
|
||||
|
||||
if let fieldKey = model.fieldKey {
|
||||
self.fieldKey = fieldKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
|
||||
82
MVMCoreUI/Atoms/TextFields/EntryFieldModel.swift
Normal file
82
MVMCoreUI/Atoms/TextFields/EntryFieldModel.swift
Normal file
@ -0,0 +1,82 @@
|
||||
//
|
||||
// EntryFieldModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 1/22/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
@objcMembers public class EntryFieldModel: MoleculeModelProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public class var identifier: String {
|
||||
return ""
|
||||
}
|
||||
|
||||
public var backgroundColor: Color?
|
||||
public var moleculeName: String?
|
||||
public var title: String?
|
||||
public var feedback: String?
|
||||
public var errorMessage: String = ""
|
||||
public var isEnabled: Bool = true
|
||||
public var isLocked: Bool?
|
||||
public var isSelected: Bool?
|
||||
public var fieldKey: String?
|
||||
public var isValid: Bool?
|
||||
public var isRequired: Bool?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case backgroundColor
|
||||
case title
|
||||
case isEnabled
|
||||
case feedback
|
||||
case errorMessage = "errorMsg"
|
||||
case isLocked
|
||||
case isSelected
|
||||
case fieldKey
|
||||
case isValid
|
||||
case isRequired = "required"
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
moleculeName = try typeContainer.decodeIfPresent(String.self, forKey: .moleculeName)
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
title = try typeContainer.decodeIfPresent(String.self, forKey: .title)
|
||||
feedback = try typeContainer.decodeIfPresent(String.self, forKey: .feedback)
|
||||
errorMessage = try typeContainer.decodeIfPresent(String.self, forKey: .errorMessage) ?? ""
|
||||
isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .isEnabled) ?? true
|
||||
isLocked = try typeContainer.decodeIfPresent(Bool.self, forKey: .isLocked)
|
||||
isSelected = try typeContainer.decodeIfPresent(Bool.self, forKey: .isSelected)
|
||||
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
|
||||
isValid = try typeContainer.decodeIfPresent(Bool.self, forKey: .isValid)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(moleculeName, forKey: .moleculeName)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(title, forKey: .title)
|
||||
try container.encodeIfPresent(feedback, forKey: .feedback)
|
||||
try container.encode(errorMessage, forKey: .errorMessage)
|
||||
try container.encode(isEnabled, forKey: .isEnabled)
|
||||
try container.encode(isLocked, forKey: .isLocked)
|
||||
try container.encode(isSelected, forKey: .isSelected)
|
||||
try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
|
||||
try container.encodeIfPresent(isValid, forKey: .isValid)
|
||||
}
|
||||
}
|
||||
@ -21,7 +21,7 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
|
||||
public var setInitialValueInTextField = true
|
||||
|
||||
/// Closure passed here will run as picker changes items.
|
||||
public var observeDropdownChange: ((String)->())?
|
||||
public var observeDropdownChange: ((String, String)->())?
|
||||
|
||||
/// Closure passed here will run upon dismissing the selection picker.
|
||||
public var observeDropdownSelection: ((String)->())?
|
||||
@ -90,6 +90,19 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
|
||||
observeDropdownSelection?(pickerData[pickerIndex])
|
||||
}
|
||||
}
|
||||
|
||||
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
|
||||
guard let model = model as? ItemDropdownEntryFieldModel else { return }
|
||||
|
||||
pickerData = model.options
|
||||
setPickerDelegates(delegate: self)
|
||||
|
||||
if let pickerView = pickerView {
|
||||
self.pickerView(pickerView, didSelectRow: 0, inComponent: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- Base Picker Delegate
|
||||
@ -104,12 +117,16 @@ extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource {
|
||||
}
|
||||
|
||||
@objc public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
|
||||
guard !pickerData.isEmpty else { return nil }
|
||||
|
||||
return pickerData[row]
|
||||
}
|
||||
|
||||
@objc public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
|
||||
guard !pickerData.isEmpty else { return }
|
||||
|
||||
observeDropdownChange?(text ?? "", pickerData[row])
|
||||
text = pickerData[row]
|
||||
observeDropdownChange?(pickerData[row])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
45
MVMCoreUI/Atoms/TextFields/ItemDropdownEntryFieldModel.swift
Normal file
45
MVMCoreUI/Atoms/TextFields/ItemDropdownEntryFieldModel.swift
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// ItemDropdownEntryFieldModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 1/22/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
@objcMembers public class ItemDropdownEntryFieldModel: BaseDropdownEntryFieldModel {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override class var identifier: String {
|
||||
return "dropDown"
|
||||
}
|
||||
|
||||
public var options: [String] = []
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case options
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
options = try typeContainer.decode([String].self, forKey: .options)
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encode(options, forKey: .options)
|
||||
}
|
||||
}
|
||||
79
MVMCoreUI/Atoms/TextFields/MFTextField+ModelExtension.swift
Normal file
79
MVMCoreUI/Atoms/TextFields/MFTextField+ModelExtension.swift
Normal file
@ -0,0 +1,79 @@
|
||||
//
|
||||
// MFTextField+ModelExtension.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Suresh, Kamlesh on 11/19/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
enum TextType: String {
|
||||
case dropDown = "dropDown"
|
||||
case password = "password"
|
||||
case number = "number"
|
||||
case email = "email"
|
||||
}
|
||||
extension MFTextField: ModelMoleculeViewProtocol {
|
||||
//
|
||||
public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
//TODO: Need to create setWithModel in ViewConstraining View
|
||||
#warning("This below call should be repaced with super.setWithModel once we get rid of ViewConstrainingView.")
|
||||
//TODO: This below call should be repaced with super.setWithModel once we get rid of ViewConstrainingView.
|
||||
setUpDefaultWithModel(model, delegateObject, additionalData)
|
||||
|
||||
guard let textFieldModel = model as? TextFieldModel,
|
||||
let delegateObject = delegateObject else {
|
||||
return
|
||||
}
|
||||
|
||||
if let delegate = delegateObject.formValidationProtocol {
|
||||
let formValidator = FormValidator.getFormValidatorFor(delegate: delegate)
|
||||
mfTextFieldDelegate = formValidator
|
||||
uiTextFieldDelegate = delegateObject.uiTextFieldDelegate
|
||||
if let textField = textField {
|
||||
MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: uiTextFieldDelegate)
|
||||
}
|
||||
}
|
||||
|
||||
formText = textFieldModel.label as NSString?
|
||||
text = textFieldModel.value as NSString?
|
||||
if let disabled = textFieldModel.disabled {
|
||||
enable(disabled)
|
||||
}
|
||||
errMessage = textFieldModel.errorMsg
|
||||
fieldKey = textFieldModel.fieldKey
|
||||
groupName = textFieldModel.groupName
|
||||
|
||||
|
||||
switch textFieldModel.type {
|
||||
case TextType.dropDown.rawValue:
|
||||
dropDownCarrotLabel?.isHidden = true
|
||||
hasDropDown = true
|
||||
break
|
||||
case TextType.password.rawValue:
|
||||
textField?.isSecureTextEntry = true
|
||||
break
|
||||
case TextType.number.rawValue:
|
||||
textField?.keyboardType = .numberPad
|
||||
break
|
||||
case TextType.email.rawValue:
|
||||
textField?.keyboardType = .emailAddress
|
||||
break
|
||||
default:
|
||||
print("default")
|
||||
}
|
||||
|
||||
if let regex = textFieldModel.regex {
|
||||
validationBlock = {(enteredValue: String?) -> Bool in
|
||||
if let enteredValue = enteredValue {
|
||||
return MVMCoreUIUtility.validate(enteredValue, withRegularExpression: regex)
|
||||
}
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
setDefaultValidationBlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
MVMCoreUI/Atoms/TextFields/MdnEntryFieldModel.swift
Normal file
17
MVMCoreUI/Atoms/TextFields/MdnEntryFieldModel.swift
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// MdnEntryFieldModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 1/22/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
@objcMembers public class MdnEntryFieldModel: TextEntryFieldModel {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override class var identifier: String {
|
||||
return "mdnEntryField"
|
||||
}
|
||||
}
|
||||
@ -270,6 +270,49 @@ import UIKit
|
||||
|
||||
resignFirstResponder()
|
||||
}
|
||||
|
||||
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
|
||||
guard let model = model as? TextEntryFieldModel else { return }
|
||||
|
||||
FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol)
|
||||
|
||||
textColor.enabled = model.enabledTextColor?.uiColor
|
||||
textColor.disabled = model.disabledTextColor?.uiColor
|
||||
text = model.text
|
||||
placeholder = model.placeholder
|
||||
|
||||
switch model.type {
|
||||
case "password":
|
||||
textField.isSecureTextEntry = true
|
||||
|
||||
case "number":
|
||||
textField.keyboardType = .numberPad
|
||||
|
||||
case "email":
|
||||
textField.keyboardType = .emailAddress
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if let regex = model.regex, !regex.isEmpty {
|
||||
validationBlock = { enteredValue in
|
||||
guard let value = enteredValue else { return false }
|
||||
return MVMCoreUIUtility.validate(value, withRegularExpression: regex)
|
||||
}
|
||||
} else {
|
||||
defaultValidationBlock()
|
||||
}
|
||||
|
||||
if let formValidationProtocol = delegateObject?.formValidationProtocol {
|
||||
observingTextFieldDelegate = FormValidator.getFormValidatorFor(delegate: formValidationProtocol)
|
||||
}
|
||||
|
||||
uiTextFieldDelegate = delegateObject?.uiTextFieldDelegate
|
||||
MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: uiTextFieldDelegate)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
|
||||
66
MVMCoreUI/Atoms/TextFields/TextEntryFieldModel.swift
Normal file
66
MVMCoreUI/Atoms/TextFields/TextEntryFieldModel.swift
Normal file
@ -0,0 +1,66 @@
|
||||
//
|
||||
// TextEntryFieldModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 1/22/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
@objcMembers public class TextEntryFieldModel: EntryFieldModel {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public override class var identifier: String {
|
||||
return "textField"
|
||||
}
|
||||
|
||||
public var text: String?
|
||||
public var placeholder: String?
|
||||
public var enabledTextColor: Color?
|
||||
public var disabledTextColor: Color?
|
||||
public var type: String?
|
||||
public var regex: String?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case text
|
||||
case placeholder
|
||||
case enabledTextColor
|
||||
case disabledTextColor
|
||||
case type
|
||||
case regex
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
text = try typeContainer.decodeIfPresent(String.self, forKey: .text)
|
||||
placeholder = try typeContainer.decodeIfPresent(String.self, forKey: .placeholder)
|
||||
enabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledTextColor)
|
||||
disabledTextColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledTextColor)
|
||||
type = try typeContainer.decodeIfPresent(String.self, forKey: .type)
|
||||
regex = try typeContainer.decodeIfPresent(String.self, forKey: .regex)
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encodeIfPresent(text, forKey: .text)
|
||||
try container.encodeIfPresent(placeholder, forKey: .placeholder)
|
||||
try container.encodeIfPresent(enabledTextColor, forKey: .enabledTextColor)
|
||||
try container.encodeIfPresent(disabledTextColor, forKey: .disabledTextColor)
|
||||
try container.encodeIfPresent(type, forKey: .type)
|
||||
try container.encodeIfPresent(regex, forKey: .regex)
|
||||
}
|
||||
}
|
||||
27
MVMCoreUI/Atoms/TextFields/TextFieldModel.swift
Normal file
27
MVMCoreUI/Atoms/TextFields/TextFieldModel.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// TextFieldModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Suresh, Kamlesh on 10/24/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class TextFieldModel: MoleculeModelProtocol, FormModelProtocol {
|
||||
|
||||
public static var identifier: String = "textField"
|
||||
public var backgroundColor: Color?
|
||||
public var moleculeName: String
|
||||
public var editable: Bool?
|
||||
public var disabled: Bool?
|
||||
public var errorMsg: String?
|
||||
public var label: String?
|
||||
public var type: String?
|
||||
public var value: String?
|
||||
public var regex: String?
|
||||
|
||||
public var required: Bool?
|
||||
public var fieldKey: String?
|
||||
public var groupName: String?
|
||||
}
|
||||
@ -178,29 +178,27 @@ open class CaretView: View {
|
||||
//------------------------------------------------------
|
||||
|
||||
// Default values for view.
|
||||
@objc open func setAsMolecule() {
|
||||
@objc open override func setAsMolecule() {
|
||||
defaultState()
|
||||
}
|
||||
|
||||
@objc open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
|
||||
guard let dictionary = json else { return }
|
||||
|
||||
if let strokeColorHex = dictionary["strokeColor"] as? String {
|
||||
strokeColor = UIColor.mfGet(forHex: strokeColorHex)
|
||||
public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: CaretViewModel.self) else { return }
|
||||
setWithModel(model, delegateObject, additionalData)
|
||||
}
|
||||
|
||||
//MARK: - MVMCoreMoleculeViewProtocol
|
||||
override public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
guard let caretModel = model as? CaretViewModel else {
|
||||
return
|
||||
}
|
||||
|
||||
if let isHidden = dictionary[KeyIsHidden] as? Bool {
|
||||
self.isHidden = isHidden
|
||||
}
|
||||
|
||||
if let isOpaque = dictionary[KeyIsOpaque] as? Bool {
|
||||
self.isOpaque = isOpaque
|
||||
}
|
||||
|
||||
if let lineWidth = dictionary["lineWidth"] as? CGFloat {
|
||||
self.lineWidth = lineWidth
|
||||
strokeColor = caretModel.strokeColor.uiColor
|
||||
isHidden = caretModel.isHidden ?? false
|
||||
isOpaque = caretModel.isOpaque ?? false
|
||||
|
||||
if let lineWidthValue = caretModel.lineWidth {
|
||||
lineWidth = lineWidthValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
47
MVMCoreUI/Atoms/Views/CaretViewModel.swift
Normal file
47
MVMCoreUI/Atoms/Views/CaretViewModel.swift
Normal file
@ -0,0 +1,47 @@
|
||||
//
|
||||
// CaretViewModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Ryan on 11/20/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers public class CaretViewModel: MoleculeModelProtocol {
|
||||
|
||||
public static var identifier: String = "caretView"
|
||||
public var backgroundColor: Color?
|
||||
public var strokeColor: Color = Color(uiColor: .black)
|
||||
public var isHidden: Bool?
|
||||
public var isOpaque: Bool?
|
||||
public var lineWidth: CGFloat?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case backgroundColor
|
||||
case strokeColor
|
||||
case isHidden
|
||||
case isOpaque
|
||||
case lineWidth
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
if let strokeColor = try typeContainer.decodeIfPresent(Color.self, forKey: .strokeColor) {
|
||||
self.strokeColor = strokeColor
|
||||
}
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
isHidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .isHidden)
|
||||
isOpaque = try typeContainer.decodeIfPresent(Bool.self, forKey: .isOpaque)
|
||||
lineWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .lineWidth)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(strokeColor, forKey: .strokeColor)
|
||||
try container.encodeIfPresent(isHidden, forKey: .isHidden)
|
||||
try container.encodeIfPresent(isOpaque, forKey: .isOpaque)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(lineWidth, forKey: .lineWidth)
|
||||
}
|
||||
}
|
||||
@ -76,6 +76,27 @@ import MVMCore
|
||||
}
|
||||
}
|
||||
|
||||
open override var isEnabled: Bool {
|
||||
didSet {
|
||||
|
||||
isUserInteractionEnabled = isEnabled
|
||||
|
||||
if isEnabled {
|
||||
layer.borderColor = borderColor.cgColor
|
||||
backgroundColor = isSelected ? checkedBackgroundColor : unCheckedBackgroundColor
|
||||
setShapeLayerStrokeColor(checkColor)
|
||||
} else {
|
||||
layer.borderColor = disabledBorderColor.cgColor
|
||||
backgroundColor = disabledBackgroundColor
|
||||
setShapeLayerStrokeColor(disabledCheckColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var disabledBackgroundColor: UIColor = .clear
|
||||
public var disabledBorderColor: UIColor = .mvmCoolGray3
|
||||
public var disabledCheckColor: UIColor = .mvmCoolGray3
|
||||
|
||||
/// Color of the check mark.
|
||||
public var checkColor: UIColor = .black {
|
||||
didSet {
|
||||
@ -107,7 +128,6 @@ import MVMCore
|
||||
if !updateSelectionOnly {
|
||||
layoutIfNeeded()
|
||||
shapeLayer?.removeAllAnimations()
|
||||
|
||||
updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated)
|
||||
FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol)
|
||||
updateAccessibilityLabel()
|
||||
@ -154,16 +174,12 @@ import MVMCore
|
||||
|
||||
public convenience init(isChecked: Bool) {
|
||||
self.init(frame: .zero)
|
||||
updateSelectionOnly = true
|
||||
isSelected = isChecked
|
||||
updateSelectionOnly = false
|
||||
checkAndBypassAnimations(selected: isChecked)
|
||||
}
|
||||
|
||||
public convenience init(checkedBackgroundColor: UIColor, unCheckedBackgroundColor: UIColor, isChecked: Bool = false) {
|
||||
self.init(frame: .zero)
|
||||
updateSelectionOnly = true
|
||||
isSelected = isChecked
|
||||
updateSelectionOnly = false
|
||||
checkAndBypassAnimations(selected: isChecked)
|
||||
self.checkedBackgroundColor = checkedBackgroundColor
|
||||
self.unCheckedBackgroundColor = unCheckedBackgroundColor
|
||||
}
|
||||
@ -177,8 +193,6 @@ import MVMCore
|
||||
|
||||
drawShapeLayer()
|
||||
layer.cornerRadius = isRound ? cornerRadiusValue : 0
|
||||
layer.borderWidth = borderWidth
|
||||
layer.borderColor = borderColor.cgColor
|
||||
}
|
||||
|
||||
open override func setupView() {
|
||||
@ -228,7 +242,7 @@ import MVMCore
|
||||
self.shapeLayer = shapeLayer
|
||||
shapeLayer.frame = bounds
|
||||
layer.addSublayer(shapeLayer)
|
||||
shapeLayer.strokeColor = checkColor.cgColor
|
||||
shapeLayer.strokeColor = isEnabled ? checkColor.cgColor : disabledCheckColor.cgColor
|
||||
shapeLayer.fillColor = UIColor.clear.cgColor
|
||||
shapeLayer.path = checkMarkPath()
|
||||
shapeLayer.lineJoin = .miter
|
||||
@ -268,9 +282,7 @@ import MVMCore
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
self.updateSelectionOnly = true
|
||||
self.isSelected = selected
|
||||
self.updateSelectionOnly = false
|
||||
self.checkAndBypassAnimations(selected: selected)
|
||||
self.drawShapeLayer()
|
||||
self.shapeLayer?.removeAllAnimations()
|
||||
self.updateCheckboxUI(isSelected: selected, isAnimated: animated)
|
||||
@ -313,23 +325,6 @@ import MVMCore
|
||||
}
|
||||
}
|
||||
|
||||
func isEnabled(_ enabled: Bool) {
|
||||
|
||||
isUserInteractionEnabled = enabled
|
||||
|
||||
if enabled {
|
||||
layer.borderColor = borderColor.cgColor
|
||||
backgroundColor = isSelected ? checkedBackgroundColor : unCheckedBackgroundColor
|
||||
alpha = 1.0
|
||||
setShapeLayerStrokeColor(checkColor)
|
||||
} else {
|
||||
layer.borderColor = UIColor.mfSilver().cgColor
|
||||
backgroundColor = .clear
|
||||
alpha = DisableOppacity
|
||||
setShapeLayerStrokeColor(UIColor.mfSilver())
|
||||
}
|
||||
}
|
||||
|
||||
private func setShapeLayerStrokeColor(_ color: UIColor) {
|
||||
|
||||
if let shapeLayer = shapeLayer {
|
||||
@ -345,13 +340,19 @@ import MVMCore
|
||||
widthConstraint?.isActive = isActive
|
||||
}
|
||||
|
||||
private func checkAndBypassAnimations(selected: Bool) {
|
||||
updateSelectionOnly = true
|
||||
isSelected = selected
|
||||
updateSelectionOnly = false
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - UITouch
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
|
||||
sendActions(for: .touchUpInside)
|
||||
sendActions(for: .touchUpInside)
|
||||
}
|
||||
|
||||
override open func accessibilityActivate() -> Bool {
|
||||
@ -370,7 +371,7 @@ import MVMCore
|
||||
open override func reset() {
|
||||
super.reset()
|
||||
|
||||
isEnabled(true)
|
||||
isEnabled = true
|
||||
shapeLayer?.removeAllAnimations()
|
||||
shapeLayer?.removeFromSuperlayer()
|
||||
shapeLayer = nil
|
||||
@ -379,9 +380,7 @@ import MVMCore
|
||||
borderWidth = 1.0
|
||||
checkColor = .black
|
||||
checkWidth = 2.0
|
||||
updateSelectionOnly = true
|
||||
isSelected = false
|
||||
updateSelectionOnly = false
|
||||
checkAndBypassAnimations(selected: false)
|
||||
}
|
||||
|
||||
open func setAsMolecule() {
|
||||
@ -424,16 +423,14 @@ import MVMCore
|
||||
layer.borderWidth = borderWidth
|
||||
}
|
||||
|
||||
if let isChecked = dictionary["isChecked"] as? Bool, isChecked {
|
||||
updateSelectionOnly = true
|
||||
isSelected = isChecked
|
||||
updateSelectionOnly = false
|
||||
}
|
||||
|
||||
if let checkColorHex = dictionary["checkColor"] as? String {
|
||||
checkColor = UIColor.mfGet(forHex: checkColorHex)
|
||||
}
|
||||
|
||||
if let isChecked = dictionary["isChecked"] as? Bool, isChecked {
|
||||
checkAndBypassAnimations(selected: isChecked)
|
||||
}
|
||||
|
||||
if let unCheckedBackgroundColorHex = dictionary["unCheckedBackgroundColor"] as? String {
|
||||
unCheckedBackgroundColor = UIColor.mfGet(forHex: unCheckedBackgroundColorHex)
|
||||
}
|
||||
@ -451,13 +448,57 @@ import MVMCore
|
||||
}
|
||||
|
||||
if let enabled = dictionary["isEnabled"] as? Bool {
|
||||
isEnabled(enabled)
|
||||
isEnabled = enabled
|
||||
}
|
||||
|
||||
if let actionMap = dictionary.optionalDictionaryForKey("actionMap") {
|
||||
if let actionMap = dictionary.optionalDictionaryForKey("action") {
|
||||
actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) }
|
||||
}
|
||||
}
|
||||
|
||||
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
|
||||
guard let model = model as? CheckboxModel else { return }
|
||||
|
||||
self.delegateObject = delegateObject
|
||||
FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol)
|
||||
|
||||
groupName = model.groupName
|
||||
fieldValue = model.value
|
||||
isRequired = model.required
|
||||
|
||||
if let fieldKey = model.fieldKey {
|
||||
self.fieldKey = fieldKey
|
||||
}
|
||||
|
||||
borderColor = model.borderColor.uiColor
|
||||
borderWidth = model.borderWidth
|
||||
|
||||
checkColor = model.checkColor.uiColor
|
||||
unCheckedBackgroundColor = model.unCheckedBackgroundColor.uiColor
|
||||
checkedBackgroundColor = model.checkedBackgroundColor.uiColor
|
||||
disabledCheckColor = model.disabledCheckColor.uiColor
|
||||
disabledBorderColor = model.disabledBorderColor.uiColor
|
||||
disabledBackgroundColor = model.disabledBackgroundColor.uiColor
|
||||
|
||||
isAnimated = model.isAnimated
|
||||
isRound = model.isRound
|
||||
|
||||
if model.isChecked {
|
||||
checkAndBypassAnimations(selected: model.isChecked)
|
||||
}
|
||||
|
||||
isEnabled = model.isEnabled
|
||||
|
||||
if let action = model.action {
|
||||
actionBlock = {
|
||||
if let actionMap = action.toJSON() {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- FormValidationProtocol
|
||||
|
||||
24
MVMCoreUI/Atoms/Views/CheckboxLabelModel.swift
Normal file
24
MVMCoreUI/Atoms/Views/CheckboxLabelModel.swift
Normal file
@ -0,0 +1,24 @@
|
||||
//
|
||||
// CheckboxWithLabelViewModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Chintakrinda, Arun Kumar (Arun) on 21/01/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum CheckboxPosition: String, Codable {
|
||||
case center
|
||||
case top
|
||||
case bottom
|
||||
}
|
||||
|
||||
@objcMembers public class CheckboxLabelModel: MoleculeModelProtocol {
|
||||
public static var identifier: String = "checkboxLabel"
|
||||
public var backgroundColor: Color?
|
||||
|
||||
public var checkboxAlignment: CheckboxPosition?
|
||||
public var checkbox: CheckboxModel
|
||||
public var label: LabelModel
|
||||
}
|
||||
106
MVMCoreUI/Atoms/Views/CheckboxModel.swift
Normal file
106
MVMCoreUI/Atoms/Views/CheckboxModel.swift
Normal file
@ -0,0 +1,106 @@
|
||||
//
|
||||
// CheckboxModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Chintakrinda, Arun Kumar (Arun) on 21/01/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers public class CheckboxModel: MoleculeModelProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public static var identifier: String = "checkbox"
|
||||
public var backgroundColor: Color?
|
||||
|
||||
public var groupName: String?
|
||||
public var value: String?
|
||||
public var fieldKey: String?
|
||||
public var required: Bool = false
|
||||
public var borderColor: Color = Color(uiColor: .black)
|
||||
public var borderWidth: CGFloat = 1
|
||||
public var isChecked: Bool = false
|
||||
public var checkColor: Color = Color(uiColor: .black)
|
||||
public var unCheckedBackgroundColor: Color = Color(uiColor: .clear)
|
||||
public var checkedBackgroundColor: Color = Color(uiColor: .clear)
|
||||
public var isAnimated: Bool = true
|
||||
public var isRound: Bool = false
|
||||
public var isEnabled: Bool = true
|
||||
public var action: ActionModelProtocol?
|
||||
public var disabledBackgroundColor: Color = Color(uiColor: .clear)
|
||||
public var disabledBorderColor: Color = Color(uiColor: .mvmCoolGray3)
|
||||
public var disabledCheckColor: Color = Color(uiColor: .mvmCoolGray3)
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Keys
|
||||
//--------------------------------------------------
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case groupName
|
||||
case value
|
||||
case fieldKey
|
||||
case required
|
||||
case borderColor
|
||||
case borderWidth
|
||||
case isChecked
|
||||
case checkColor
|
||||
case unCheckedBackgroundColor
|
||||
case checkedBackgroundColor
|
||||
case disabledBackgroundColor
|
||||
case disabledCheckColor
|
||||
case disabledBorderColor
|
||||
case isAnimated
|
||||
case isRound
|
||||
case isEnabled
|
||||
case action
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codec
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName)
|
||||
value = try typeContainer.decodeIfPresent(String.self, forKey: .value)
|
||||
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
|
||||
required = try typeContainer.decodeIfPresent(Bool.self, forKey: .required) ?? false
|
||||
borderWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .borderWidth) ?? 1
|
||||
borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) ?? Color(uiColor: .black)
|
||||
checkColor = try typeContainer.decodeIfPresent(Color.self, forKey: .checkColor) ?? Color(uiColor: .black)
|
||||
unCheckedBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .unCheckedBackgroundColor) ?? Color(uiColor: .clear)
|
||||
checkedBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .checkedBackgroundColor) ?? Color(uiColor: .clear)
|
||||
disabledBackgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBackgroundColor) ?? Color(uiColor: .clear)
|
||||
disabledBorderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledBorderColor) ?? Color(uiColor: .mvmCoolGray3)
|
||||
disabledCheckColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledCheckColor) ?? Color(uiColor: .mvmCoolGray3)
|
||||
isChecked = try typeContainer.decodeIfPresent(Bool.self, forKey: .isChecked) ?? false
|
||||
isAnimated = try typeContainer.decodeIfPresent(Bool.self, forKey: .isAnimated) ?? true
|
||||
isRound = try typeContainer.decodeIfPresent(Bool.self, forKey: .isRound) ?? false
|
||||
isEnabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .isEnabled) ?? true
|
||||
action = try typeContainer.decodeModelIfPresent(codingKey: .action, typeCodingKey: ActionCodingKey.actionType)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(groupName, forKey: .groupName)
|
||||
try container.encodeIfPresent(value, forKey: .value)
|
||||
try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
|
||||
try container.encodeIfPresent(required, forKey: .required)
|
||||
try container.encodeIfPresent(borderColor, forKey: .borderColor)
|
||||
try container.encode(borderWidth, forKey: .borderWidth)
|
||||
try container.encode(isChecked, forKey: .isChecked)
|
||||
try container.encodeIfPresent(checkColor, forKey: .checkColor)
|
||||
try container.encodeIfPresent(unCheckedBackgroundColor, forKey: .unCheckedBackgroundColor)
|
||||
try container.encodeIfPresent(checkedBackgroundColor, forKey: .checkedBackgroundColor)
|
||||
try container.encodeIfPresent(disabledBorderColor, forKey: .disabledBorderColor)
|
||||
try container.encodeIfPresent(disabledBackgroundColor, forKey: .disabledBackgroundColor)
|
||||
try container.encodeIfPresent(disabledCheckColor, forKey: .disabledCheckColor)
|
||||
try container.encodeIfPresent(isAnimated, forKey: .isAnimated)
|
||||
try container.encodeIfPresent(isRound, forKey: .isRound)
|
||||
try container.encodeIfPresent(isEnabled, forKey: .isEnabled)
|
||||
try container.encodeModelIfPresent(action, forKey: .action)
|
||||
}
|
||||
}
|
||||
@ -21,12 +21,6 @@
|
||||
|
||||
public var checkboxPosition: CheckboxPosition = .center
|
||||
|
||||
public enum CheckboxPosition: String {
|
||||
case center
|
||||
case top
|
||||
case bottom
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
//--------------------------------------------------
|
||||
@ -120,6 +114,17 @@
|
||||
checkboxCenterYConstraint?.isActive = false
|
||||
}
|
||||
}
|
||||
|
||||
open override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
guard let checkBoxWithLabelModel = model as? CheckboxLabelModel else { return }
|
||||
|
||||
if let checkboxAlignment = checkBoxWithLabelModel.checkboxAlignment {
|
||||
alignCheckbox(checkboxAlignment)
|
||||
}
|
||||
|
||||
checkbox.setWithModel(checkBoxWithLabelModel.checkbox, delegateObject, additionalData)
|
||||
label.setWithModel(checkBoxWithLabelModel.label, delegateObject, additionalData)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Molecular
|
||||
|
||||
128
MVMCoreUI/Atoms/Views/CircleProgressModel.swift
Normal file
128
MVMCoreUI/Atoms/Views/CircleProgressModel.swift
Normal file
@ -0,0 +1,128 @@
|
||||
//
|
||||
// CircleProgressModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 1/13/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public enum GraphSize: String, Codable {
|
||||
case small, medium, large
|
||||
}
|
||||
|
||||
public enum GraphStyle: String, Codable {
|
||||
case unlimited, safetyMode
|
||||
}
|
||||
|
||||
public class CircleProgressModel: MoleculeModelProtocol {
|
||||
public static var identifier: String = "circleProgress"
|
||||
public var style: GraphStyle = .unlimited {
|
||||
didSet {
|
||||
updateStyle()
|
||||
}
|
||||
}
|
||||
|
||||
public var size: GraphSize = .small {
|
||||
didSet {
|
||||
updateSize()
|
||||
}
|
||||
}
|
||||
public var diameter: CGFloat = 24
|
||||
public var lineWidth: CGFloat = 5
|
||||
public var clockwise: Bool = true
|
||||
public var duration : Double = 1.0
|
||||
public var colors = [Color]()
|
||||
public var backgroundColor: Color?
|
||||
|
||||
public init() {}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case style
|
||||
case size
|
||||
case diameter
|
||||
case lineWidth
|
||||
case clockwise
|
||||
case duration
|
||||
case colors
|
||||
case backgroundColor
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
if let style = try typeContainer.decodeIfPresent(GraphStyle.self, forKey: .style) {
|
||||
self.style = style
|
||||
}
|
||||
if let size = try typeContainer.decodeIfPresent(GraphSize.self, forKey: .size) {
|
||||
self.size = size
|
||||
}
|
||||
if let diameter = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .diameter) {
|
||||
self.diameter = diameter
|
||||
}
|
||||
if let lineWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .lineWidth) {
|
||||
self.lineWidth = lineWidth
|
||||
}
|
||||
if let clockwise = try typeContainer.decodeIfPresent(Bool.self, forKey: .clockwise) {
|
||||
self.clockwise = clockwise
|
||||
}
|
||||
if let duration = try typeContainer.decodeIfPresent(Double.self, forKey: .duration) {
|
||||
self.duration = duration
|
||||
}
|
||||
if let colors = try typeContainer.decodeIfPresent([Color].self, forKey: .colors) {
|
||||
self.colors = colors
|
||||
}
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(style, forKey: .style)
|
||||
try container.encode(size, forKey: .size)
|
||||
try container.encode(diameter, forKey: .diameter)
|
||||
try container.encode(lineWidth, forKey: .lineWidth)
|
||||
try container.encode(clockwise, forKey: .clockwise)
|
||||
try container.encode(duration, forKey: .duration)
|
||||
try container.encode(colors, forKey: .colors)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
}
|
||||
|
||||
func getCGColorsFromArray(_ colorArray: [String]) -> [Color] {
|
||||
return colorArray.map { (colorString) -> Color in
|
||||
return Color(uiColor: UIColor.mfGet(forHex: colorString))
|
||||
}
|
||||
}
|
||||
|
||||
func updateStyle() {
|
||||
switch style {
|
||||
case .unlimited:
|
||||
duration = 1.0
|
||||
clockwise = true
|
||||
//current style, only the end part shows darker look
|
||||
colors = getCGColorsFromArray(["#007AB8","#007AB8","#033554"])
|
||||
break
|
||||
case .safetyMode:
|
||||
duration = 1.5
|
||||
clockwise = true
|
||||
colors = getCGColorsFromArray(["#CC4D0F","#CC4D0F","AB0309"])
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func updateSize() {
|
||||
switch size {
|
||||
case .small:
|
||||
diameter = MFSizeObject(standardSize: 20)?.getValueBasedOnApplicationWidth() ?? 20
|
||||
lineWidth = MFSizeObject(standardSize: 4)?.getValueBasedOnApplicationWidth() ?? 4
|
||||
break
|
||||
case .medium:
|
||||
diameter = MFSizeObject(standardSize: 100)?.getValueBasedOnApplicationWidth() ?? 100
|
||||
lineWidth = MFSizeObject(standardSize: 8)?.getValueBasedOnApplicationWidth() ?? 8
|
||||
break
|
||||
case .large:
|
||||
diameter = MFSizeObject(standardSize: 180)?.getValueBasedOnApplicationWidth() ?? 180
|
||||
lineWidth = MFSizeObject(standardSize: 12)?.getValueBasedOnApplicationWidth() ?? 12
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -15,7 +15,14 @@ open class DashLine: View {
|
||||
// MARK: - Properties
|
||||
//------------------------------------------------------
|
||||
|
||||
@objc public var dashColor: UIColor?
|
||||
var dashModel: DashLineModel? {
|
||||
get { return model as? DashLineModel }
|
||||
}
|
||||
|
||||
//TODO: Need this for BAU. Can remove once we fix BAU
|
||||
public var dashColor: UIColor?
|
||||
|
||||
@objc private var dashLayer: CAShapeLayer?
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - Initializer
|
||||
@ -64,9 +71,10 @@ open class DashLine: View {
|
||||
dashLayer.lineCap = .round
|
||||
dashLayer.lineDashPattern = [NSNumber(value: 2), NSNumber(value: 2)]
|
||||
dashLayer.path = path.cgPath
|
||||
dashLayer.strokeColor = dashColor?.cgColor ?? UIColor.mfLighterGray().cgColor
|
||||
dashLayer.strokeColor = dashModel?.dashColor.cgColor ?? dashColor?.cgColor
|
||||
dashLayer.fillColor = UIColor.clear.cgColor
|
||||
dashLayer.backgroundColor = backgroundColor?.cgColor ?? UIColor.white.cgColor
|
||||
self.dashLayer = dashLayer
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
@ -74,23 +82,27 @@ open class DashLine: View {
|
||||
//------------------------------------------------------
|
||||
|
||||
// Default values for view.
|
||||
@objc open func setAsMolecule() {
|
||||
@objc open override func reset() {
|
||||
backgroundColor = .clear
|
||||
isHidden = false
|
||||
}
|
||||
|
||||
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
|
||||
// Configure class properties with JSON values
|
||||
guard let jsonDictionary = json else { return }
|
||||
|
||||
if let isHiddenValue = jsonDictionary[KeyIsHidden] as? Bool {
|
||||
guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: DashLineModel.self) else { return }
|
||||
setWithModel(model, delegateObject, additionalData)
|
||||
}
|
||||
|
||||
//MARK: - MVMCoreMoleculeViewProtocol
|
||||
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
guard let dashLineModel = dashModel else {
|
||||
return
|
||||
}
|
||||
if let isHiddenValue = dashLineModel.isHidden {
|
||||
isHidden = isHiddenValue
|
||||
}
|
||||
|
||||
if let dashColorHex = jsonDictionary["dashColor"] as? String {
|
||||
dashColor = UIColor.mfGet(forHex: dashColorHex)
|
||||
if let backgroundColor = dashLineModel.backgroundColor {
|
||||
dashLayer?.backgroundColor = backgroundColor.uiColor.cgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
43
MVMCoreUI/Atoms/Views/DashLineModel.swift
Normal file
43
MVMCoreUI/Atoms/Views/DashLineModel.swift
Normal file
@ -0,0 +1,43 @@
|
||||
//
|
||||
// DashLineModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Ryan on 11/20/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers public class DashLineModel: MoleculeModelProtocol {
|
||||
public static var identifier: String = "dashLine"
|
||||
public var backgroundColor: Color?
|
||||
|
||||
public var dashColor: Color = Color(uiColor: .mfLighterGray())
|
||||
public var isHidden: Bool?
|
||||
|
||||
public init(dashColor: Color) {
|
||||
self.dashColor = dashColor
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case backgroundColor
|
||||
case dashColor
|
||||
case isHidden
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
if let dashColor = try typeContainer.decodeIfPresent(Color.self, forKey: .dashColor) {
|
||||
self.dashColor = dashColor
|
||||
}
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
isHidden = try typeContainer.decodeIfPresent(Bool.self, forKey: .isHidden)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(dashColor, forKey: .dashColor)
|
||||
try container.encodeIfPresent(isHidden, forKey: .isHidden)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
//
|
||||
// DropDown.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 11/26/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class DropDown: MFTextField {
|
||||
let picker = MVMCoreUICommonViewsUtility.commonPickerView()!
|
||||
|
||||
public override func getNib() -> UINib? {
|
||||
return UINib(nibName: String(describing: MFTextField.self), bundle: MVMCoreUIUtility.bundleForMVMCoreUI())
|
||||
}
|
||||
|
||||
public override func setupView() {
|
||||
super.setupView()
|
||||
dropDownCarrotWidth?.isActive = false
|
||||
errorHeightConstraint?.constant = 0
|
||||
}
|
||||
|
||||
override public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
guard let textField = textField, textField.inputView == nil else { return }
|
||||
picker.delegate = self
|
||||
picker.dataSource = self
|
||||
picker.tag = textField.tag
|
||||
textField.inputView = picker
|
||||
picker.reloadAllComponents()
|
||||
MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: delegateObject?.uiTextFieldDelegate)
|
||||
textField.text = json?.stringWithChainOfKeysOrIndexes(["options",picker.selectedRow(inComponent: 0)])
|
||||
}
|
||||
}
|
||||
|
||||
extension DropDown: UIPickerViewDelegate {
|
||||
public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
|
||||
return json?.stringWithChainOfKeysOrIndexes(["options",row])
|
||||
}
|
||||
|
||||
public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
|
||||
textField?.text = json?.stringWithChainOfKeysOrIndexes(["options",row])
|
||||
}
|
||||
}
|
||||
|
||||
extension DropDown: UIPickerViewDataSource {
|
||||
public func numberOfComponents(in pickerView: UIPickerView) -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
|
||||
return json?.optionalArrayForKey("options")?.count ?? 0
|
||||
}
|
||||
}
|
||||
@ -8,113 +8,13 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
enum GraphSize: String {
|
||||
case small, medium, large
|
||||
}
|
||||
|
||||
enum GraphStyle: String {
|
||||
case unlimited, safetyMode
|
||||
}
|
||||
|
||||
///Graph Object contains properties
|
||||
public struct GraphObject {
|
||||
|
||||
var style: GraphStyle {
|
||||
didSet {
|
||||
updateStyle()
|
||||
}
|
||||
}
|
||||
var size: GraphSize {
|
||||
didSet {
|
||||
updateSize()
|
||||
}
|
||||
}
|
||||
var diameter: CGFloat = 24
|
||||
var lineWidth: CGFloat = 5
|
||||
var clockwise: Bool = true
|
||||
var duration : Double = 1.0
|
||||
var colors = [CGColor]()
|
||||
|
||||
public init(_ json: [AnyHashable : Any]?) {
|
||||
style = .unlimited
|
||||
size = .small
|
||||
guard let json = json else {
|
||||
return
|
||||
}
|
||||
if let styleString = json.optionalStringForKey("style") {
|
||||
style = GraphStyle(rawValue: styleString) ?? .unlimited
|
||||
}
|
||||
if let sizeString = json.optionalStringForKey("size") {
|
||||
size = GraphSize(rawValue: sizeString) ?? .small
|
||||
}
|
||||
updateStyle()
|
||||
updateSize()
|
||||
if let diameter = json.optionalCGFloatForKey("diameter") {
|
||||
self.diameter = diameter
|
||||
}
|
||||
if let lineWidth = json.optionalCGFloatForKey("lineWidth") {
|
||||
self.lineWidth = lineWidth
|
||||
}
|
||||
if let clockwise = json.optionalBoolForKey("clockwise") {
|
||||
self.clockwise = clockwise
|
||||
}
|
||||
if let duration = json["duration"] as? Double {
|
||||
self.duration = duration
|
||||
}
|
||||
if let colorArray = json.optionalArrayForKey("colors") as? [String] {
|
||||
colors = getCGColorsFromArray(colorArray)
|
||||
}
|
||||
}
|
||||
|
||||
func getCGColorsFromArray(_ colorArray: [String]) -> [CGColor] {
|
||||
return colorArray.map { (colorString) -> CGColor in
|
||||
return UIColor.mfGet(forHex: colorString).cgColor
|
||||
}
|
||||
}
|
||||
|
||||
mutating func updateStyle() {
|
||||
switch style {
|
||||
case .unlimited:
|
||||
duration = 1.0
|
||||
clockwise = true
|
||||
//current style, only the end part shows darker look
|
||||
colors = getCGColorsFromArray(["#007AB8","#007AB8","#033554"])
|
||||
break
|
||||
case .safetyMode:
|
||||
duration = 1.5
|
||||
clockwise = true
|
||||
colors = getCGColorsFromArray(["#CC4D0F","#CC4D0F","AB0309"])
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
//those are
|
||||
mutating func updateSize() {
|
||||
switch size {
|
||||
case .small:
|
||||
diameter = MFSizeObject(standardSize: 20)?.getValueBasedOnApplicationWidth() ?? 20
|
||||
lineWidth = MFSizeObject(standardSize: 4)?.getValueBasedOnApplicationWidth() ?? 4
|
||||
break
|
||||
case .medium:
|
||||
diameter = MFSizeObject(standardSize: 100)?.getValueBasedOnApplicationWidth() ?? 100
|
||||
lineWidth = MFSizeObject(standardSize: 8)?.getValueBasedOnApplicationWidth() ?? 8
|
||||
break
|
||||
case .large:
|
||||
diameter = MFSizeObject(standardSize: 180)?.getValueBasedOnApplicationWidth() ?? 180
|
||||
lineWidth = MFSizeObject(standardSize: 12)?.getValueBasedOnApplicationWidth() ?? 12
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objcMembers open class GraphView: View, MVMCoreUIViewConstrainingProtocol {
|
||||
|
||||
var heightConstraint: NSLayoutConstraint?
|
||||
var gradientLayer: CALayer?
|
||||
var graphObject: GraphObject?
|
||||
|
||||
var graphModel: CircleProgressModel? {
|
||||
return model as? CircleProgressModel
|
||||
}
|
||||
|
||||
// MARK: setup
|
||||
open override func setupView() {
|
||||
@ -125,13 +25,17 @@ public struct GraphObject {
|
||||
heightConstraint?.isActive = true
|
||||
widthAnchor.constraint(equalTo: heightAnchor).isActive = true
|
||||
}
|
||||
|
||||
override open func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
let object = GraphObject(json)
|
||||
graphObject = object
|
||||
createGraphCircle(object)
|
||||
rotationAnimation(object)
|
||||
|
||||
override open func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
guard let model = model as? CircleProgressModel else { return }
|
||||
createGraphCircle(model)
|
||||
rotationAnimation(model)
|
||||
}
|
||||
|
||||
override open func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: CircleProgressModel.self) else { return }
|
||||
setWithModel(model, delegateObject, additionalData)
|
||||
}
|
||||
|
||||
class func getAngle(_ piValue: Double) -> Double {
|
||||
@ -143,7 +47,7 @@ public struct GraphObject {
|
||||
}
|
||||
|
||||
// MARK: circle
|
||||
open func createGraphCircle(_ graphObject: GraphObject) {
|
||||
open func createGraphCircle(_ graphObject: CircleProgressModel) {
|
||||
if let sublayers = layer.sublayers {
|
||||
for sublayer in sublayers {
|
||||
sublayer.removeAllAnimations()
|
||||
@ -184,14 +88,14 @@ public struct GraphObject {
|
||||
| | |
|
||||
-------------
|
||||
*/
|
||||
func createGradientLayer(_ graphObject: GraphObject) -> CALayer {
|
||||
func createGradientLayer(_ graphObject: CircleProgressModel) -> CALayer {
|
||||
let containLayer = CALayer()
|
||||
containLayer.frame = CGRect(x: 0, y: 0, width: graphObject.diameter, height: graphObject.diameter)
|
||||
let radius = graphObject.diameter / 2.0
|
||||
|
||||
//create graident layers
|
||||
guard graphObject.colors.count > 1 else {
|
||||
containLayer.backgroundColor = graphObject.colors.first
|
||||
containLayer.backgroundColor = graphObject.colors.first?.uiColor.cgColor
|
||||
return containLayer
|
||||
}
|
||||
var topGradientHeight : CGFloat = 0.0
|
||||
@ -208,9 +112,11 @@ public struct GraphObject {
|
||||
//if number of colors is even, need to display gradient layer, otherwise make top layer as solid color layer
|
||||
if graphObject.colors.count % 2 == 0 {
|
||||
leftColors.removeLast()
|
||||
topLayer.colors = [leftColors.last!, rightColors.first!]
|
||||
let firstColor = leftColors.last!.uiColor.cgColor
|
||||
let secondColor = rightColors.first!.uiColor.cgColor
|
||||
topLayer.colors = [firstColor, secondColor]
|
||||
} else {
|
||||
topLayer.backgroundColor = leftColors.last
|
||||
topLayer.backgroundColor = leftColors.last?.uiColor.cgColor
|
||||
}
|
||||
containLayer.addSublayer(topLayer)
|
||||
|
||||
@ -221,9 +127,11 @@ public struct GraphObject {
|
||||
|
||||
//count of graidentLayer.colors must be bigger than 1, otherwise set backgroundColor
|
||||
if leftColors.count > 1 {
|
||||
leftLayer.colors = Array(leftColors)
|
||||
leftLayer.colors = leftColors.map({ (color) -> CGColor in
|
||||
return color.uiColor.cgColor
|
||||
})
|
||||
} else {
|
||||
leftLayer.backgroundColor = leftColors.first
|
||||
leftLayer.backgroundColor = leftColors.first?.uiColor.cgColor
|
||||
}
|
||||
containLayer.addSublayer(leftLayer)
|
||||
|
||||
@ -232,9 +140,11 @@ public struct GraphObject {
|
||||
rightLayer.startPoint = CGPoint(x: 0, y: 0)
|
||||
rightLayer.endPoint = CGPoint(x: 0, y: 1)
|
||||
if rightColors.count > 1 {
|
||||
rightLayer.colors = Array(rightColors)
|
||||
rightLayer.colors = rightColors.map({ (color) -> CGColor in
|
||||
return color.uiColor.cgColor
|
||||
})
|
||||
} else {
|
||||
rightLayer.backgroundColor = rightColors.first
|
||||
rightLayer.backgroundColor = rightColors.first?.uiColor.cgColor
|
||||
}
|
||||
containLayer.addSublayer(rightLayer)
|
||||
|
||||
@ -246,7 +156,7 @@ public struct GraphObject {
|
||||
}
|
||||
|
||||
//MARK: Animation
|
||||
func rotationAnimation(_ object: GraphObject) {
|
||||
func rotationAnimation(_ object: CircleProgressModel) {
|
||||
MVMCoreDispatchUtility.performBlock(onMainThread:{
|
||||
let rotation = CABasicAnimation(keyPath: "transform.rotation")
|
||||
let animationHandler = GraphViewAnimationHandler.shared
|
||||
@ -277,7 +187,7 @@ public struct GraphObject {
|
||||
|
||||
extension GraphView: CAAnimationDelegate {
|
||||
public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
|
||||
if let object = graphObject {
|
||||
if let object = graphModel {
|
||||
rotationAnimation(object)
|
||||
}
|
||||
}
|
||||
|
||||
21
MVMCoreUI/Atoms/Views/ImageViewModel.swift
Normal file
21
MVMCoreUI/Atoms/Views/ImageViewModel.swift
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// ImageViewModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Ryan on 11/20/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers public class ImageViewModel: MoleculeModelProtocol {
|
||||
public static var identifier: String = "image"
|
||||
public var backgroundColor: Color?
|
||||
|
||||
public var image: String
|
||||
public var accessibilityText: String?
|
||||
public var fallbackImage: String?
|
||||
public var imageFormat: String?
|
||||
public var width: CGFloat?
|
||||
public var height: CGFloat?
|
||||
}
|
||||
@ -12,7 +12,8 @@ import MVMCore
|
||||
public typealias ActionBlock = () -> ()
|
||||
|
||||
|
||||
@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol {
|
||||
@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol, ModelMoleculeViewProtocol {
|
||||
|
||||
//------------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//------------------------------------------------------
|
||||
@ -212,6 +213,123 @@ public typealias ActionBlock = () -> ()
|
||||
}
|
||||
}
|
||||
|
||||
enum LabelAlignment: String {
|
||||
case center
|
||||
case right
|
||||
case left
|
||||
}
|
||||
|
||||
public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
clauses = []
|
||||
guard let labelModel = model as? LabelModel else { return }
|
||||
attributedText = nil
|
||||
text = labelModel.text
|
||||
Label.setLabel(self, withHTML: labelModel.html)
|
||||
let alignment = LabelAlignment(rawValue: labelModel.textAlignment ?? "")
|
||||
switch alignment {
|
||||
case .center:
|
||||
textAlignment = .center
|
||||
case .right:
|
||||
textAlignment = .right
|
||||
default:
|
||||
textAlignment = .left
|
||||
}
|
||||
|
||||
makeWholeViewClickable = labelModel.makeWholeViewClickable ?? false
|
||||
if let backgroundColor = labelModel.backgroundColor {
|
||||
self.backgroundColor = backgroundColor.uiColor
|
||||
}
|
||||
if let accessibilityText = labelModel.accessibilityText {
|
||||
accessibilityLabel = accessibilityText
|
||||
}
|
||||
if let fontStyle = labelModel.fontStyle {
|
||||
MFStyler.styleLabel(self, withStyle: fontStyle)
|
||||
MFStyler.styleLabel(self, withStyle: fontStyle, genericScaling: false)
|
||||
standardFontSize = font.pointSize
|
||||
} else {
|
||||
let fontSize = labelModel.fontSize
|
||||
if let fontSize = fontSize {
|
||||
standardFontSize = fontSize
|
||||
}
|
||||
if let fontName = labelModel.fontName {
|
||||
font = MFFonts.mfFont(withName: fontName, size: fontSize ?? standardFontSize)
|
||||
} else if let fontSize = fontSize {
|
||||
font = font.withSize(fontSize)
|
||||
}
|
||||
}
|
||||
|
||||
if let textColorHex = labelModel.textColor, !textColorHex.isEmpty {
|
||||
textColor = UIColor.mfGet(forHex: textColorHex)
|
||||
}
|
||||
|
||||
if let attributes = labelModel.attributes, let labelText = text {
|
||||
let attributedString = NSMutableAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font.withSize(standardFontSize), NSAttributedString.Key.foregroundColor: textColor as UIColor])
|
||||
for attribute in attributes {
|
||||
let range = NSRange(location: attribute.location, length: attribute.length)
|
||||
switch attribute {
|
||||
case _ as LabelAttributeUnderlineModel:
|
||||
attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range)
|
||||
case _ as LabelAttributeStrikeThroughModel:
|
||||
attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range)
|
||||
attributedString.addAttribute(.baselineOffset, value: 0, range: range)
|
||||
case let colorAtt as LabelAttributeColorModel:
|
||||
if let colorHex = colorAtt.textColor, !colorHex.isEmpty {
|
||||
attributedString.removeAttribute(.foregroundColor, range: range)
|
||||
attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range)
|
||||
}
|
||||
case let imageAtt as LabelAttributeImageModel:
|
||||
var fontSize = font.pointSize
|
||||
if let attributeSize = imageAtt.size {
|
||||
fontSize = attributeSize
|
||||
}
|
||||
let imageName = imageAtt.name ?? "externalLink"
|
||||
let imageAttachment: NSTextAttachment
|
||||
|
||||
if let url = imageAtt.URL {
|
||||
imageAttachment = Label.getTextAttachmentFrom(url: url, dimension: fontSize, label: self)
|
||||
} else {
|
||||
imageAttachment = Label.getTextAttachmentImage(name: imageName, dimension: fontSize)
|
||||
}
|
||||
let mutableString = NSMutableAttributedString()
|
||||
mutableString.append(NSAttributedString(attachment: imageAttachment))
|
||||
attributedString.insert(mutableString, at: imageAtt.location)
|
||||
case let fontAtt as LabelAttributeFontModel:
|
||||
if let fontStyle = fontAtt.style {
|
||||
let styles = MFStyler.styleGetAttributedString("0", withStyle: fontStyle)
|
||||
attributedString.removeAttribute(.font, range: range)
|
||||
attributedString.removeAttribute(.foregroundColor, range: range)
|
||||
attributedString.addAttributes(styles.attributes(at: 0, effectiveRange: nil), range: range)
|
||||
} else {
|
||||
let fontSize = fontAtt.size
|
||||
var font: UIFont?
|
||||
|
||||
if let fontName = fontAtt.name {
|
||||
font = MFFonts.mfFont(withName: fontName, size: fontSize ?? self.font.pointSize)
|
||||
} else if let fontSize = fontSize {
|
||||
font = self.font.withSize(fontSize)
|
||||
}
|
||||
if let font = font {
|
||||
attributedString.removeAttribute(.font, range: range)
|
||||
attributedString.addAttribute(.font, value: font, range: range)
|
||||
}
|
||||
}
|
||||
case let actionAtt as LabelAttributeActionModel:
|
||||
addTappableLinkAttribute(range: NSRange(location: range.location, length: range.length)) {
|
||||
if let data = try? actionAtt.action.encode(using: JSONEncoder()), let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any] {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
}
|
||||
addActionAttributes(range: range, string: attributedString)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
attributedText = attributedString
|
||||
originalAttributedString = attributedText
|
||||
hero = labelModel.hero
|
||||
}
|
||||
}
|
||||
|
||||
@objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
guard let label = label else { return }
|
||||
|
||||
32
MVMCoreUI/Atoms/Views/Label/LabelAttributeActionModel.swift
Normal file
32
MVMCoreUI/Atoms/Views/Label/LabelAttributeActionModel.swift
Normal file
@ -0,0 +1,32 @@
|
||||
//
|
||||
// LabelAttributeActionModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Ryan on 11/21/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class LabelAttributeActionModel: LabelAttributeModel {
|
||||
override public class var identifier: String {
|
||||
return "action"
|
||||
}
|
||||
var action: ActionModelProtocol
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
action = try typeContainer.decodeModel(codingKey: .action, typeCodingKey: ActionCodingKey.actionType)
|
||||
try super.init(from: decoder)
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeModel(action, forKey: .action)
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case action
|
||||
}
|
||||
}
|
||||
35
MVMCoreUI/Atoms/Views/Label/LabelAttributeColorModel.swift
Normal file
35
MVMCoreUI/Atoms/Views/Label/LabelAttributeColorModel.swift
Normal file
@ -0,0 +1,35 @@
|
||||
//
|
||||
// LabelAttributeColorModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Ryan on 11/21/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class LabelAttributeColorModel: LabelAttributeModel {
|
||||
|
||||
override public class var identifier: String {
|
||||
return "color"
|
||||
}
|
||||
|
||||
var textColor: String?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case textColor
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.textColor = try typeContainer.decodeIfPresent(String.self, forKey: .textColor)
|
||||
try super.init(from: decoder)
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(textColor, forKey: .textColor)
|
||||
}
|
||||
}
|
||||
41
MVMCoreUI/Atoms/Views/Label/LabelAttributeFontModel.swift
Normal file
41
MVMCoreUI/Atoms/Views/Label/LabelAttributeFontModel.swift
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// LabelAttributeFontModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Ryan on 11/21/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class LabelAttributeFontModel: LabelAttributeModel {
|
||||
override public class var identifier: String {
|
||||
return "font"
|
||||
}
|
||||
|
||||
var style: String?
|
||||
var name: String?
|
||||
var size: CGFloat?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case style
|
||||
case name
|
||||
case size
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.style = try typeContainer.decodeIfPresent(String.self, forKey: .style)
|
||||
self.name = try typeContainer.decodeIfPresent(String.self, forKey: .name)
|
||||
self.size = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .size)
|
||||
try super.init(from: decoder)
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(style, forKey: .style)
|
||||
try container.encodeIfPresent(name, forKey: .name)
|
||||
try container.encodeIfPresent(size, forKey: .size)
|
||||
}
|
||||
}
|
||||
42
MVMCoreUI/Atoms/Views/Label/LabelAttributeImageModel.swift
Normal file
42
MVMCoreUI/Atoms/Views/Label/LabelAttributeImageModel.swift
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// LabelAttributeImageModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Ryan on 11/21/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class LabelAttributeImageModel: LabelAttributeModel {
|
||||
|
||||
override public class var identifier: String {
|
||||
return "image"
|
||||
}
|
||||
|
||||
var size: CGFloat?
|
||||
var name: String?
|
||||
var URL: String?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case size
|
||||
case name
|
||||
case URL
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.size = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .size)
|
||||
self.name = try typeContainer.decodeIfPresent(String.self, forKey: .name)
|
||||
self.URL = try typeContainer.decodeIfPresent(String.self, forKey: .URL)
|
||||
try super.init(from: decoder)
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(size, forKey: .size)
|
||||
try container.encodeIfPresent(name, forKey: .name)
|
||||
try container.encodeIfPresent(URL, forKey: .URL)
|
||||
}
|
||||
}
|
||||
41
MVMCoreUI/Atoms/Views/Label/LabelAttributeModel.swift
Normal file
41
MVMCoreUI/Atoms/Views/Label/LabelAttributeModel.swift
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// LabelAttributeModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Ryan on 11/21/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers open class LabelAttributeModel: Model {
|
||||
|
||||
public class var identifier: String {
|
||||
return ""
|
||||
}
|
||||
|
||||
var type: String
|
||||
var location: Int
|
||||
var length: Int
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case location
|
||||
case length
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.type = try typeContainer.decode(String.self, forKey: .type)
|
||||
self.location = try typeContainer.decode(Int.self, forKey: .location)
|
||||
self.length = try typeContainer.decode(Int.self, forKey: .length)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(type, forKey: .type)
|
||||
try container.encode(location, forKey: .location)
|
||||
try container.encode(length, forKey: .length)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
//
|
||||
// LabelAttributeStrikeThroughModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Ryan on 11/21/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class LabelAttributeStrikeThroughModel: LabelAttributeModel {
|
||||
override public class var identifier: String {
|
||||
return "strikethrough"
|
||||
}
|
||||
required public init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
//
|
||||
// LabelAttributeUnderlineModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Ryan on 11/21/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class LabelAttributeUnderlineModel: LabelAttributeModel {
|
||||
override public class var identifier: String {
|
||||
return "underline"
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
try super.init(from: decoder)
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
}
|
||||
}
|
||||
85
MVMCoreUI/Atoms/Views/Label/LabelModel.swift
Normal file
85
MVMCoreUI/Atoms/Views/Label/LabelModel.swift
Normal file
@ -0,0 +1,85 @@
|
||||
//
|
||||
// Label.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Suresh, Kamlesh on 10/3/19.
|
||||
// Copyright © 2019 Suresh, Kamlesh. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers public class LabelModel: MoleculeModelProtocol {
|
||||
public static var identifier: String = "label"
|
||||
public var moleculeName: String?
|
||||
public var backgroundColor: Color?
|
||||
|
||||
public var text: String
|
||||
public var accessibilityText: String?
|
||||
public var textColor: String?
|
||||
public var fontStyle: String?
|
||||
public var fontName: String?
|
||||
public var fontSize: CGFloat?
|
||||
public var textAlignment: String?
|
||||
public var attributes: [LabelAttributeModel]?
|
||||
public var html: String?
|
||||
public var hero: Int?
|
||||
public var makeWholeViewClickable: Bool?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case text
|
||||
case accessibilityText
|
||||
case textColor
|
||||
case backgroundColor
|
||||
case fontStyle
|
||||
case fontName
|
||||
case fontSize
|
||||
case textAlignment
|
||||
case attributes
|
||||
case html
|
||||
case hero
|
||||
case makeWholeViewClickable
|
||||
}
|
||||
|
||||
enum AttributeTypeKey: String, CodingKey {
|
||||
case type
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.moleculeName = try typeContainer.decodeIfPresent(String.self, forKey: .moleculeName)
|
||||
self.text = try typeContainer.decode(String.self, forKey: .text)
|
||||
self.accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText)
|
||||
self.textColor = try typeContainer.decodeIfPresent(String.self, forKey: .textColor)
|
||||
self.backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
self.fontStyle = try typeContainer.decodeIfPresent(String.self, forKey: .fontStyle)
|
||||
self.fontName = try typeContainer.decodeIfPresent(String.self, forKey: .fontName)
|
||||
self.fontSize = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .fontSize)
|
||||
self.textAlignment = try typeContainer.decodeIfPresent(String.self, forKey: .textAlignment)
|
||||
self.attributes = try typeContainer.decodeModelsIfPresent(codingKey: .attributes, typeCodingKey: AttributeTypeKey.type) as? [LabelAttributeModel]
|
||||
self.html = try typeContainer.decodeIfPresent(String.self, forKey: .html)
|
||||
self.hero = try typeContainer.decodeIfPresent(Int.self, forKey: .hero)
|
||||
self.makeWholeViewClickable = try typeContainer.decodeIfPresent(Bool.self, forKey: .makeWholeViewClickable)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(moleculeName, forKey: .moleculeName)
|
||||
try container.encode(text, forKey: .text)
|
||||
try container.encodeIfPresent(accessibilityText, forKey: .accessibilityText)
|
||||
try container.encodeIfPresent(textColor, forKey: .textColor)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(fontStyle, forKey: .fontStyle)
|
||||
try container.encodeIfPresent(fontName, forKey: .fontName)
|
||||
try container.encodeIfPresent(fontSize, forKey: .fontSize)
|
||||
try container.encodeIfPresent(textAlignment, forKey: .textAlignment)
|
||||
var attributeContainer = container.nestedUnkeyedContainer(forKey: .attributes)
|
||||
try attributes?.forEach { attributeModel in
|
||||
try attributeContainer.encode(attributeModel)
|
||||
}
|
||||
try container.encodeIfPresent(html, forKey: .html)
|
||||
try container.encodeIfPresent(hero, forKey: .hero)
|
||||
try container.encodeIfPresent(makeWholeViewClickable, forKey: .makeWholeViewClickable)
|
||||
}
|
||||
}
|
||||
16
MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift
Normal file
16
MVMCoreUI/Atoms/Views/LeftRightLabelModel.swift
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// LeftRightLabelModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Ryan on 12/12/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class LeftRightLabelModel: MoleculeModelProtocol {
|
||||
public static var identifier: String = "leftRightLabelView"
|
||||
public var backgroundColor: Color?
|
||||
public var leftText: LabelModel
|
||||
public var rightText: LabelModel?
|
||||
}
|
||||
@ -9,7 +9,7 @@
|
||||
import Foundation
|
||||
|
||||
|
||||
@objcMembers open class LeftRightLabelView: ViewConstrainingView {
|
||||
@objcMembers open class LeftRightLabelView: View {
|
||||
//------------------------------------------------------
|
||||
// MARK: - Outlets
|
||||
//------------------------------------------------------
|
||||
@ -28,8 +28,8 @@ import Foundation
|
||||
// MARK: - Initialization
|
||||
//------------------------------------------------------
|
||||
|
||||
public init() {
|
||||
super.init(frame: .zero)
|
||||
public convenience init() {
|
||||
self.init(frame: .zero)
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
@ -176,4 +176,20 @@ import Foundation
|
||||
constrainLeftLabel()
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - MVMCoreMoleculeViewProtocol
|
||||
|
||||
open override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
guard let leftRightLabelModel = model as? LeftRightLabelModel else {
|
||||
return
|
||||
}
|
||||
leftTextLabel.setWithModel(leftRightLabelModel.leftText, delegateObject, additionalData)
|
||||
rightTextLabel.setWithModel(leftRightLabelModel.rightText, delegateObject, additionalData)
|
||||
if !leftTextLabel.hasText {
|
||||
constrainRightLabel()
|
||||
} else if !rightTextLabel.hasText {
|
||||
constrainLeftLabel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,42 +9,35 @@
|
||||
import UIKit
|
||||
|
||||
@objcMembers open class Line: View {
|
||||
var lineModel: LineModel? {
|
||||
get { return model as? LineModel }
|
||||
}
|
||||
|
||||
public var heightConstraint: NSLayoutConstraint?
|
||||
|
||||
public enum Style: String, Codable {
|
||||
case standard
|
||||
case thin
|
||||
case medium
|
||||
case heavy
|
||||
case none
|
||||
}
|
||||
|
||||
public var style = Style.standard {
|
||||
didSet {
|
||||
switch style {
|
||||
case .standard:
|
||||
heightConstraint?.constant = 1
|
||||
backgroundColor = .mfSilver()
|
||||
case .thin:
|
||||
heightConstraint?.constant = 1
|
||||
backgroundColor = .black
|
||||
case .medium:
|
||||
heightConstraint?.constant = 2
|
||||
backgroundColor = .black
|
||||
case .heavy:
|
||||
heightConstraint?.constant = 4
|
||||
backgroundColor = .black
|
||||
case .none:
|
||||
heightConstraint?.constant = 0
|
||||
}
|
||||
open func setStyle(_ style: LineModel.Style) {
|
||||
switch style {
|
||||
case .standard:
|
||||
heightConstraint?.constant = 1
|
||||
backgroundColor = .mfSilver()
|
||||
case .thin:
|
||||
heightConstraint?.constant = 1
|
||||
backgroundColor = .black
|
||||
case .medium:
|
||||
heightConstraint?.constant = 2
|
||||
backgroundColor = .black
|
||||
case .heavy:
|
||||
heightConstraint?.constant = 4
|
||||
backgroundColor = .black
|
||||
case .none:
|
||||
heightConstraint?.constant = 0
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
open func shouldBeVisible() -> Bool {
|
||||
guard let type = json?.optionalStringForKey(KeyType) else { return false }
|
||||
return type != "none"
|
||||
guard let type = lineModel?.type else { return false }
|
||||
return type != .none
|
||||
}
|
||||
|
||||
public convenience init(pinTo view: UIView, edge: UIRectEdge, useMargin: Bool) {
|
||||
@ -56,26 +49,33 @@ import UIKit
|
||||
// MARK: - MVMCoreViewProtocol
|
||||
open override func setupView() {
|
||||
super.setupView()
|
||||
backgroundColor = .black
|
||||
heightConstraint = heightAnchor.constraint(equalToConstant: 1)
|
||||
heightConstraint?.isActive = true
|
||||
setStyle(.standard)
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
|
||||
|
||||
// If no type, default to standard.
|
||||
if let typeString = json?.optionalStringForKey(KeyType), let type = Style.init(rawValue: typeString) {
|
||||
style = type
|
||||
if let typeString = json?.optionalStringForKey(KeyType), let type = LineModel.Style.init(rawValue: typeString) {
|
||||
setStyle(type)
|
||||
} else {
|
||||
style = .standard
|
||||
setStyle(.standard)
|
||||
}
|
||||
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
}
|
||||
|
||||
open override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
if let lineModel = model as? LineModel {
|
||||
setStyle(lineModel.type ?? .standard)
|
||||
}
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
}
|
||||
|
||||
open override func reset() {
|
||||
style = .standard
|
||||
setStyle(.standard)
|
||||
}
|
||||
|
||||
open func copyBackgroundColor() -> Bool {
|
||||
@ -83,7 +83,7 @@ import UIKit
|
||||
}
|
||||
|
||||
public static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
|
||||
guard let type = json?.optionalStringForKey(KeyType), let style = Style(rawValue: type) else { return 1 }
|
||||
guard let type = json?.optionalStringForKey(KeyType), let style = LineModel.Style(rawValue: type) else { return 1 }
|
||||
switch style {
|
||||
case .none:
|
||||
return 0
|
||||
|
||||
81
MVMCoreUI/Atoms/Views/LineModel.swift
Normal file
81
MVMCoreUI/Atoms/Views/LineModel.swift
Normal file
@ -0,0 +1,81 @@
|
||||
//
|
||||
// LineModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Suresh, Kamlesh on 10/28/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers public class LineModel: MoleculeModelProtocol {
|
||||
|
||||
/*
|
||||
The frequency of the line in a moleculeList.
|
||||
all (between all cells, above top, below bottom)
|
||||
allExceptTop (between all cells, below bottom)
|
||||
allExceptBottom (between all cells, above top)
|
||||
between (between all cells)
|
||||
*/
|
||||
public enum Frequency: String, Codable {
|
||||
case all
|
||||
case allExceptTop
|
||||
case allExceptBottom
|
||||
case between
|
||||
}
|
||||
|
||||
/*
|
||||
The style of the line.
|
||||
standard (1 height, silver)
|
||||
thin (1 height, black)
|
||||
medium (2 height, black)
|
||||
heavy (4 height, black)
|
||||
none (hidden)
|
||||
*/
|
||||
public enum Style: String, Codable {
|
||||
case standard
|
||||
case thin
|
||||
case medium
|
||||
case heavy
|
||||
case none
|
||||
}
|
||||
|
||||
public static var identifier: String = "line"
|
||||
public var type: Style = .standard
|
||||
public var frequency: Frequency? = .allExceptTop
|
||||
|
||||
//TODO: use color insted of backgroundColor. Needs server changes
|
||||
// public var color: Color?
|
||||
public var backgroundColor: Color?
|
||||
|
||||
public init(type: Style) {
|
||||
self.type = type
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case type
|
||||
case backgroundColor
|
||||
case color
|
||||
case frequency
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
if let type = try typeContainer.decodeIfPresent(Style.self, forKey: .type) {
|
||||
self.type = type
|
||||
}
|
||||
if let frequency = try typeContainer.decodeIfPresent(Frequency.self, forKey: .frequency) {
|
||||
self.frequency = frequency
|
||||
}
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encode(type, forKey: .type)
|
||||
try container.encodeIfPresent(frequency, forKey: .frequency)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers open class MFLoadImageView: ViewConstrainingView {
|
||||
@objcMembers open class MFLoadImageView: ViewConstrainingView, ModelMoleculeViewProtocol {
|
||||
public let loadingSpinner = MFLoadingSpinner(frame: .zero)
|
||||
public let imageView = MFTransparentGIFView(frame: .zero)
|
||||
public var addSizeConstraintsForAspectRatio = false
|
||||
@ -209,6 +209,31 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
guard let imageModel = model as? ImageViewModel else {
|
||||
return
|
||||
}
|
||||
if let accessibilityString = imageModel.accessibilityText {
|
||||
imageView.accessibilityLabel = accessibilityString
|
||||
imageView.accessibilityTraits = .staticText
|
||||
imageView.isAccessibilityElement = true
|
||||
}
|
||||
let width = imageModel.width ?? imageWidth
|
||||
let height = imageModel.height ?? imageHeight
|
||||
// For smoother transitions, set constraints that we know immediately.
|
||||
if let width = width, addSizeConstraintsForAspectRatio {
|
||||
setWidth(width)
|
||||
}
|
||||
if let height = height, addSizeConstraintsForAspectRatio {
|
||||
setHeight(height)
|
||||
}
|
||||
if shouldLoadImage(withName: imageModel.image, width: width, height: height) {
|
||||
imageView.image = nil
|
||||
imageView.animatedImage = nil
|
||||
loadImage(withName: imageModel.image, format: imageModel.imageFormat, width: width as NSNumber?, height: height as NSNumber?, customFallbackImage: imageModel.fallbackImage)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol functions
|
||||
open override func setAsMolecule() {
|
||||
addSizeConstraintsForAspectRatio = true
|
||||
@ -264,7 +289,7 @@ import UIKit
|
||||
self?.addConstraints(width: width, height: height, size: image?.size)
|
||||
self?.loadingSpinnerHeightConstraint?.constant = 0
|
||||
if layoutWillChange {
|
||||
self?.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated?(self!)
|
||||
self?.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self!)
|
||||
}
|
||||
completionHandler(image,data,isFallbackImage)
|
||||
})}
|
||||
|
||||
24
MVMCoreUI/Atoms/Views/MFView+ModelExtension.swift
Normal file
24
MVMCoreUI/Atoms/Views/MFView+ModelExtension.swift
Normal file
@ -0,0 +1,24 @@
|
||||
//
|
||||
// MFView+ModelExtension.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Suresh, Kamlesh on 11/20/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension MFView {
|
||||
public func setUpDefaultWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
self.model = model
|
||||
if let backgroundColor = model?.backgroundColor {
|
||||
self.backgroundColor = backgroundColor.uiColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ModelMoleculeViewProtocol where Self: MFView {
|
||||
func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
setUpDefaultWithModel(model, delegateObject, additionalData)
|
||||
}
|
||||
}
|
||||
31
MVMCoreUI/Atoms/Views/MVMCoreUISwitch+Model.swift
Normal file
31
MVMCoreUI/Atoms/Views/MVMCoreUISwitch+Model.swift
Normal file
@ -0,0 +1,31 @@
|
||||
//
|
||||
// MVMCoreUISwitch+Model.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 1/14/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// temporary until link is finished
|
||||
extension MVMCoreUISwitch: ModelMoleculeViewProtocol {
|
||||
public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) {
|
||||
guard let model = model as? ToggleModel else { return }
|
||||
|
||||
if let castSelf = self as? FormValidationProtocol {
|
||||
FormValidator.setupValidation(molecule: castSelf, delegate: delegateObject?.formValidationProtocol)
|
||||
}
|
||||
|
||||
setState(model.state, animated: false)
|
||||
|
||||
guard let action = model.action else { return }
|
||||
actionBlock = {
|
||||
if let data = try? action.encode(using: JSONEncoder()),
|
||||
let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any] {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,35 +8,13 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers open class ProgressBarObject {
|
||||
///from 0.0 to 1.0. input progress should be [0 100]
|
||||
var progress: CGFloat = 0.0
|
||||
///default color is cerulean
|
||||
var color: UIColor = UIColor.mfCerulean()
|
||||
|
||||
init(_ module: [AnyHashable: Any]?) {
|
||||
progress = (module?.optionalCGFloatForKey("progress") ?? 0.0)/100
|
||||
if let colorString = module?.optionalStringForKey("progressColor") {
|
||||
color = UIColor.mfGet(forHex: colorString)
|
||||
}
|
||||
}
|
||||
|
||||
static func getProgressBarObjectList(_ list: [[AnyHashable: Any]]?) -> [ProgressBarObject]? {
|
||||
guard list?.count ?? 0 > 0 else {
|
||||
return nil
|
||||
}
|
||||
var progressList = [ProgressBarObject]()
|
||||
for module in list! {
|
||||
let progressObject = ProgressBarObject(module)
|
||||
progressList.append(progressObject)
|
||||
}
|
||||
return progressList
|
||||
}
|
||||
}
|
||||
|
||||
@objcMembers open class MultiProgress: View {
|
||||
var multiProgressModel: MultiProgressBarModel? {
|
||||
get { return model as? MultiProgressBarModel }
|
||||
}
|
||||
|
||||
///passing value to progressList creates corresponding progress bars
|
||||
var progressList: Array<ProgressBarObject>? {
|
||||
var progressList: Array<SingleProgressBarModel>? {
|
||||
didSet {
|
||||
for subview in subviews {
|
||||
subview.removeFromSuperview()
|
||||
@ -52,7 +30,7 @@ import UIKit
|
||||
let view = UIView(frame: .zero)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(view)
|
||||
view.backgroundColor = progressObject.color
|
||||
view.backgroundColor = progressObject.progressColor.uiColor
|
||||
view.widthAnchor.constraint(equalTo: widthAnchor, multiplier: progressObject.progress).isActive = true
|
||||
view.leadingAnchor.constraint(equalTo: previous?.trailingAnchor ?? leadingAnchor).isActive = true
|
||||
previous = view
|
||||
@ -70,6 +48,7 @@ import UIKit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var thicknessConstraint: NSLayoutConstraint?
|
||||
let defaultHeight: CGFloat = 8
|
||||
|
||||
@ -84,16 +63,26 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - MVMCoreMoleculeViewProtocol
|
||||
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
guard let multiProgressModel = multiProgressModel else {
|
||||
return
|
||||
}
|
||||
roundedRect = multiProgressModel.roundedRect ?? false
|
||||
thicknessConstraint?.constant = multiProgressModel.thickness ?? defaultHeight
|
||||
progressList = multiProgressModel.progressList
|
||||
|
||||
}
|
||||
|
||||
open override func reset() {
|
||||
super.reset()
|
||||
backgroundColor = .mfLightSilver()
|
||||
progressList = nil
|
||||
}
|
||||
|
||||
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
thicknessConstraint?.constant = json?.optionalCGFloatForKey("thickness") ?? defaultHeight
|
||||
roundedRect = json?.optionalBoolForKey("roundedRect") ?? false
|
||||
progressList = ProgressBarObject.getProgressBarObjectList(json?.optionalArrayForKey("progressList") as? [[AnyHashable: Any]])
|
||||
public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
|
||||
guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: MultiProgressBarModel.self) else { return }
|
||||
setWithModel(model, delegateObject, additionalData)
|
||||
}
|
||||
}
|
||||
|
||||
54
MVMCoreUI/Atoms/Views/MultiProgressModel.swift
Normal file
54
MVMCoreUI/Atoms/Views/MultiProgressModel.swift
Normal file
@ -0,0 +1,54 @@
|
||||
//
|
||||
// MultiProgressModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Ryan on 11/20/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers public class SingleProgressBarModel: Codable {
|
||||
@Percent var progress: CGFloat
|
||||
var progressColor: Color
|
||||
|
||||
init(_ progress: CGFloat, color: Color) {
|
||||
self.progress = progress
|
||||
self.progressColor = color
|
||||
}
|
||||
}
|
||||
|
||||
@objcMembers public class MultiProgressBarModel: MoleculeModelProtocol {
|
||||
public static var identifier: String = "multiProgressBar"
|
||||
public var progressList: [SingleProgressBarModel]
|
||||
public var backgroundColor: Color?
|
||||
public var thickness: CGFloat?
|
||||
public var roundedRect: Bool?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case progressList
|
||||
case thickness
|
||||
case roundedRect
|
||||
case backgroundColor
|
||||
}
|
||||
|
||||
public init(_ progressList: [SingleProgressBarModel]) {
|
||||
self.progressList = progressList
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
progressList = try typeContainer.decode([SingleProgressBarModel].self, forKey: .progressList)
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
thickness = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .thickness)
|
||||
roundedRect = try typeContainer.decodeIfPresent(Bool.self, forKey: .roundedRect)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(progressList, forKey: .progressList)
|
||||
try container.encodeIfPresent(thickness, forKey: .thickness)
|
||||
try container.encodeIfPresent(roundedRect, forKey: .roundedRect)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
}
|
||||
}
|
||||
@ -8,12 +8,13 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers open class ProgressBar: UIProgressView, MVMCoreUIMoleculeViewProtocol, MVMCoreViewProtocol {
|
||||
var isRounded = false
|
||||
@objcMembers open class ProgressBar: UIProgressView, MVMCoreViewProtocol, ModelMoleculeViewProtocol, MVMCoreUIMoleculeViewProtocol {
|
||||
var progressBarModel: ProgressBarModel?
|
||||
|
||||
var thickness: CGFloat = 8.0 {
|
||||
willSet(newValue) {
|
||||
heightAnchor.constraint(equalToConstant: newValue).isActive = true
|
||||
if isRounded {
|
||||
if progressBarModel?.isRounded ?? false {
|
||||
layer.cornerRadius = newValue/2.0
|
||||
} else {
|
||||
progressViewStyle = .bar
|
||||
@ -40,34 +41,35 @@ import Foundation
|
||||
public func setupView() {
|
||||
clipsToBounds = true
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
reset()
|
||||
thickness = 8
|
||||
progress = 0
|
||||
progressTintColor = UIColor.mfCerulean()
|
||||
trackTintColor = UIColor.mfLightSilver()
|
||||
}
|
||||
|
||||
public func updateView(_ size: CGFloat) {
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
|
||||
if let isRounded = json?.optionalBoolForKey("roundedRect") {
|
||||
self.isRounded = isRounded
|
||||
//MARK: - MVMCoreMoleculeViewProtocol
|
||||
public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
guard let progressBarModel = model as? ProgressBarModel else {
|
||||
return
|
||||
}
|
||||
if let thickness = json?.optionalCGFloatForKey("thickness") {
|
||||
self.thickness = thickness
|
||||
}
|
||||
// as? Float returns nil, apple defect.
|
||||
if let percentage = json?["percent"] as? CGFloat {
|
||||
progress = Float(percentage/100.0)
|
||||
}
|
||||
if let progressColor = json?.optionalStringForKey("progressColor") {
|
||||
progressTintColor = UIColor.mfGet(forHex: progressColor)
|
||||
}
|
||||
if let backgroundColor = json?.optionalStringForKey("backgroundColor") {
|
||||
trackTintColor = UIColor.mfGet(forHex: backgroundColor)
|
||||
thickness = progressBarModel.thickness ?? 8
|
||||
progress = Float((progressBarModel.percent)/100.0)
|
||||
progressTintColor = progressBarModel.progressColor.uiColor
|
||||
if let backgroundColor = progressBarModel.backgroundColor {
|
||||
trackTintColor = backgroundColor.uiColor
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: ProgressBarModel.self) else { return }
|
||||
setWithModel(model, delegateObject, additionalData)
|
||||
}
|
||||
|
||||
public func reset() {
|
||||
isRounded = false
|
||||
thickness = 8
|
||||
progress = 0
|
||||
progressTintColor = UIColor.mfCerulean()
|
||||
|
||||
54
MVMCoreUI/Atoms/Views/ProgressBarModel.swift
Normal file
54
MVMCoreUI/Atoms/Views/ProgressBarModel.swift
Normal file
@ -0,0 +1,54 @@
|
||||
//
|
||||
// ProgressBarModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Ryan on 11/20/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers public class ProgressBarModel: MoleculeModelProtocol {
|
||||
public static var identifier: String = "progressBar"
|
||||
@Percent public var percent: CGFloat
|
||||
public var progressColor: Color = Color(uiColor: .mfCerulean())
|
||||
public var backgroundColor: Color? = Color(uiColor: .mfLightSilver())
|
||||
public var isRounded: Bool?
|
||||
public var thickness: CGFloat?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case isRounded = "roundRect"
|
||||
case thickness
|
||||
case percent
|
||||
case progressColor
|
||||
case backgroundColor
|
||||
}
|
||||
|
||||
public init(_ percent: CGFloat) {
|
||||
self.percent = percent
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
percent = try typeContainer.decode(CGFloat.self, forKey: .percent)
|
||||
if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .progressColor) {
|
||||
progressColor = color
|
||||
}
|
||||
if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) {
|
||||
backgroundColor = color
|
||||
}
|
||||
isRounded = try typeContainer.decodeIfPresent(Bool.self, forKey: .isRounded)
|
||||
thickness = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .thickness)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encode(percent, forKey: .percent)
|
||||
try container.encode(progressColor, forKey: .progressColor)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeIfPresent(isRounded, forKey: .isRounded)
|
||||
try container.encodeIfPresent(thickness, forKey: .thickness)
|
||||
}
|
||||
}
|
||||
@ -332,6 +332,20 @@ public typealias ActionBlockConfirmation = () -> (Bool)
|
||||
layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- ModelMoleculeViewProtocol
|
||||
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
guard let toggleModel = model as? ToggleModel else {
|
||||
return
|
||||
}
|
||||
|
||||
let toggleModelJSON = toggleModel.toJSON()
|
||||
setWithJSON(toggleModelJSON, delegateObject: delegateObject, additionalData: additionalData)
|
||||
}
|
||||
|
||||
public override class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return Self.getContainerHeight()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Accessibility
|
||||
@ -389,7 +403,7 @@ extension Toggle {
|
||||
changeStateNoAnimation(state)
|
||||
}
|
||||
|
||||
if let actionMap = dictionary.optionalDictionaryForKey("actionMap") {
|
||||
if let actionMap = dictionary.optionalDictionaryForKey("action") {
|
||||
didToggleAction = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) }
|
||||
}
|
||||
|
||||
@ -410,7 +424,7 @@ extension Toggle {
|
||||
return true
|
||||
}
|
||||
|
||||
public func alignment() -> UIStackView.Alignment {
|
||||
public func horizontalAlignment() -> UIStackView.Alignment {
|
||||
return .trailing
|
||||
}
|
||||
}
|
||||
|
||||
57
MVMCoreUI/Atoms/Views/ToggleModel.swift
Normal file
57
MVMCoreUI/Atoms/Views/ToggleModel.swift
Normal file
@ -0,0 +1,57 @@
|
||||
//
|
||||
// ToggleModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 1/14/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public class ToggleModel: MoleculeModelProtocol {
|
||||
public static var identifier: String = "toggle"
|
||||
public var moleculeName: String?
|
||||
public var backgroundColor: Color?
|
||||
public var state: Bool = true
|
||||
public var action: ActionModelProtocol?
|
||||
public var alternateAction: ActionModelProtocol?
|
||||
public var required: Bool?
|
||||
public var fieldKey: String?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case moleculeName
|
||||
case state
|
||||
case action
|
||||
case backgroundColor
|
||||
case required
|
||||
case fieldKey
|
||||
case alternateAction
|
||||
}
|
||||
|
||||
public init(_ state: Bool) {
|
||||
self.state = state
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) {
|
||||
self.state = state
|
||||
}
|
||||
action = try typeContainer.decodeModelIfPresent(codingKey: .action, typeCodingKey: ActionCodingKey.actionType)
|
||||
alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction, typeCodingKey: ActionCodingKey.actionType)
|
||||
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||
required = try typeContainer.decodeIfPresent(Bool.self, forKey: .required)
|
||||
fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||
try container.encodeModelIfPresent(action, forKey: .action)
|
||||
try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction)
|
||||
try container.encode(moleculeName, forKey: .moleculeName)
|
||||
try container.encodeIfPresent(state, forKey: .state)
|
||||
try container.encodeIfPresent(required, forKey: .required)
|
||||
try container.encodeIfPresent(fieldKey, forKey: .fieldKey)
|
||||
}
|
||||
}
|
||||
@ -6,26 +6,24 @@
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
public typealias ButtonBlock = (Button) -> ()
|
||||
public typealias ButtonAction = (Button) -> ()
|
||||
|
||||
|
||||
@objcMembers open class Button: UIButton, MFButtonProtocol {
|
||||
@objcMembers open class Button: UIButton, MFButtonProtocol, ModelMoleculeViewProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public var json: [AnyHashable: Any]?
|
||||
public var actionMap: [AnyHashable: Any]?
|
||||
open var model: MoleculeModelProtocol?
|
||||
open var actionModel: ActionModelProtocol?
|
||||
|
||||
private var initialSetupPerformed = false
|
||||
|
||||
private var buttonBlock: ButtonBlock?
|
||||
private var buttonAction: ButtonAction?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Delegate
|
||||
//--------------------------------------------------
|
||||
|
||||
public weak var buttonDelegate: ButtonDelegateProtocol?
|
||||
open weak var buttonDelegate: ButtonDelegateProtocol?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
@ -50,6 +48,7 @@ public typealias ButtonBlock = (Button) -> ()
|
||||
// MARK: - Setup
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Required to be called any init. Ensures setupView() only gets called once
|
||||
public func initialSetup() {
|
||||
|
||||
if !initialSetupPerformed {
|
||||
@ -62,61 +61,58 @@ public typealias ButtonBlock = (Button) -> ()
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
public func addBlock( event: Event, _ buttonBlock: @escaping ButtonBlock) {
|
||||
self.buttonBlock = buttonBlock
|
||||
addTarget(self, action: #selector(callBlock(_:)), for: event)
|
||||
/// Adds a block to be performed for the given event.
|
||||
open func addActionBlock(event: Event, _ buttonBlock: @escaping ButtonAction) {
|
||||
self.buttonAction = buttonBlock
|
||||
addTarget(self, action: #selector(callActionBlock(_:)), for: event)
|
||||
}
|
||||
|
||||
func callBlock(_ sender: Button) {
|
||||
buttonBlock?(self)
|
||||
@objc private func callActionBlock(_ sender: Button) {
|
||||
buttonAction?(self)
|
||||
}
|
||||
|
||||
public func setWithActionMap(_ actionMap: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
self.actionMap = actionMap
|
||||
|
||||
open func set(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
self.actionModel = actionModel
|
||||
buttonDelegate = delegateObject?.buttonDelegate
|
||||
|
||||
addBlock(event: .touchUpInside) { [weak self] sender in
|
||||
addActionBlock(event: .touchUpInside) { [weak self] sender in
|
||||
guard let self = self else { return }
|
||||
|
||||
if self.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true {
|
||||
if let data = try? actionModel.encode(using: JSONEncoder()),
|
||||
let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any],
|
||||
delegateObject?.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true {
|
||||
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
extension Button: MVMCoreUIMoleculeViewProtocol {
|
||||
|
||||
public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
self.json = json
|
||||
|
||||
guard let dictionary = json else { return }
|
||||
|
||||
if let backgroundColorString = dictionary[KeyBackgroundColor] as? String {
|
||||
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
|
||||
}
|
||||
|
||||
if let title = dictionary[KeyTitle] as? String {
|
||||
setTitle(title, for: .normal)
|
||||
// MARK:- ModelMoleculeViewProtocol
|
||||
open func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
self.model = model
|
||||
if let backgroundColor = model?.backgroundColor {
|
||||
self.backgroundColor = backgroundColor.uiColor
|
||||
}
|
||||
}
|
||||
|
||||
public func reset() {
|
||||
backgroundColor = .clear
|
||||
open class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
return model?.moleculeName
|
||||
}
|
||||
|
||||
open class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return nil
|
||||
}
|
||||
|
||||
open class func requiredModules(_ molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - MVMCoreViewProtocol
|
||||
extension Button: MVMCoreViewProtocol {
|
||||
|
||||
public func updateView(_ size: CGFloat) {}
|
||||
open func updateView(_ size: CGFloat) {}
|
||||
|
||||
/// Will be called only once.
|
||||
public func setupView() {
|
||||
|
||||
open func setupView() {
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
insetsLayoutMarginsFromSafeArea = false
|
||||
titleLabel?.numberOfLines = 0
|
||||
@ -124,9 +120,15 @@ extension Button: MVMCoreViewProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
extension Button: MVMCoreUIMoleculeViewProtocol {
|
||||
open func reset() {
|
||||
backgroundColor = .clear
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: AppleGuidelinesProtocol
|
||||
extension Button: AppleGuidelinesProtocol {
|
||||
|
||||
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
return Self.acceptablyOutsideBounds(point: point, bounds: bounds)
|
||||
}
|
||||
|
||||
@ -8,13 +8,13 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers open class Control: UIControl {
|
||||
@objcMembers open class Control: UIControl, ModelMoleculeViewProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public var json: [AnyHashable: Any]?
|
||||
|
||||
open var json: [AnyHashable: Any]?
|
||||
open var model: MoleculeModelProtocol?
|
||||
|
||||
private var initialSetupPerformed = false
|
||||
|
||||
//--------------------------------------------------
|
||||
@ -41,19 +41,36 @@ import UIKit
|
||||
//--------------------------------------------------
|
||||
|
||||
public func initialSetup() {
|
||||
|
||||
if !initialSetupPerformed {
|
||||
initialSetupPerformed = true
|
||||
setupView()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- ModelMoleculeViewProtocol
|
||||
open func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
self.model = model
|
||||
if let backgroundColor = model?.backgroundColor {
|
||||
self.backgroundColor = backgroundColor.uiColor
|
||||
}
|
||||
}
|
||||
|
||||
open class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
return model?.moleculeName
|
||||
}
|
||||
|
||||
open class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return nil
|
||||
}
|
||||
|
||||
open class func requiredModules(_ molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AppleGuidelinesProtocol
|
||||
extension Control: AppleGuidelinesProtocol {
|
||||
|
||||
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
|
||||
return Self.acceptablyOutsideBounds(point: point, bounds: bounds)
|
||||
}
|
||||
}
|
||||
@ -61,10 +78,10 @@ extension Control: AppleGuidelinesProtocol {
|
||||
// MARK: - MVMCoreViewProtocol
|
||||
extension Control: MVMCoreViewProtocol {
|
||||
|
||||
public func updateView(_ size: CGFloat) {}
|
||||
open func updateView(_ size: CGFloat) {}
|
||||
|
||||
/// Will be called only once.
|
||||
public func setupView() {
|
||||
open func setupView() {
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
insetsLayoutMarginsFromSafeArea = false
|
||||
}
|
||||
@ -72,16 +89,15 @@ extension Control: MVMCoreViewProtocol {
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
extension Control: MVMCoreUIMoleculeViewProtocol {
|
||||
open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
self.json = json
|
||||
|
||||
if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) {
|
||||
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
|
||||
}
|
||||
}
|
||||
|
||||
public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
self.json = json
|
||||
|
||||
if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) {
|
||||
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
|
||||
}
|
||||
}
|
||||
|
||||
public func reset() {
|
||||
open func reset() {
|
||||
backgroundColor = .clear
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,12 +8,9 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers open class View: UIView {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
@objcMembers open class View: UIView, ModelMoleculeViewProtocol {
|
||||
open var json: [AnyHashable: Any]?
|
||||
open var model: MoleculeModelProtocol?
|
||||
|
||||
private var initialSetupPerformed = false
|
||||
|
||||
@ -36,12 +33,31 @@ import UIKit
|
||||
}
|
||||
|
||||
public func initialSetup() {
|
||||
|
||||
if !initialSetupPerformed {
|
||||
initialSetupPerformed = true
|
||||
setupView()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- ModelMoleculeViewProtocol
|
||||
open func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
self.model = model
|
||||
if let backgroundColor = model?.backgroundColor {
|
||||
self.backgroundColor = backgroundColor.uiColor
|
||||
}
|
||||
}
|
||||
|
||||
open class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
return model?.moleculeName
|
||||
}
|
||||
|
||||
open class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
return nil
|
||||
}
|
||||
|
||||
open class func requiredModules(_ molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- MVMCoreViewProtocol
|
||||
@ -53,6 +69,7 @@ extension View: MVMCoreViewProtocol {
|
||||
open func setupView() {
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
insetsLayoutMarginsFromSafeArea = false
|
||||
MVMCoreUIUtility.setMarginsFor(self, leading: 0, top: 0, trailing: 0, bottom: 0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,4 +87,8 @@ extension View: MVMCoreUIMoleculeViewProtocol {
|
||||
open func reset() {
|
||||
backgroundColor = .clear
|
||||
}
|
||||
|
||||
open func setAsMolecule() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
49
MVMCoreUI/BaseControllers/MFViewController+Model.swift
Normal file
49
MVMCoreUI/BaseControllers/MFViewController+Model.swift
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// MFViewController+Model.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Suresh, Kamlesh on 10/24/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension MFViewController: MoleculeDelegateProtocol {
|
||||
public func getModuleWithName(_ name: String?) -> [AnyHashable : Any]? {
|
||||
guard let name = name else {
|
||||
return nil
|
||||
}
|
||||
return loadObject?.modulesJSON?.optionalDictionaryForKey(name)
|
||||
}
|
||||
|
||||
public func getModuleWithName(_ moduleName: String) -> MoleculeModelProtocol? {
|
||||
guard let moduleJSON = loadObject?.modulesJSON?.optionalDictionaryForKey(moduleName),
|
||||
let moleculeName = moduleJSON.optionalStringForKey("moleculeName"),
|
||||
let modelType = ModelRegistry.getType(for: moleculeName) as? MoleculeModelProtocol.Type else {
|
||||
return nil
|
||||
}
|
||||
do {
|
||||
return try modelType.decode(jsonDict: moduleJSON)
|
||||
} catch {
|
||||
MVMCoreUILoggingHandler.logDebugMessage(withDelegate: "error: \(error)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc public func moleculeLayoutUpdated(_ molecule: UIView & MVMCoreUIMoleculeViewProtocol) {
|
||||
|
||||
}
|
||||
|
||||
@objc public func addMolecules(_ molecules: [[AnyHashable : Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@objc public func removeMolecules(_ molecules: [[AnyHashable : Any]], sender: UITableViewCell, animation: UITableView.RowAnimation) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
public extension MFViewController {
|
||||
@objc func parsePageJSON() throws {
|
||||
}
|
||||
}
|
||||
@ -27,14 +27,13 @@
|
||||
#import <MVMCoreUI/ButtonDelegateProtocol.h>
|
||||
#import <MVMCoreUI/MFStyler.h>
|
||||
#import <MVMCoreUI/MVMCoreUIDetailViewProtocol.h>
|
||||
#import <MVMCoreUI/MoleculeDelegateProtocol.h>
|
||||
|
||||
@class MainMenuViewController;
|
||||
@class MVMCoreUITabBarPageControlViewController;
|
||||
@class MVMAnimationManager;
|
||||
@class DelegateObject;
|
||||
|
||||
@interface MFViewController : UIViewController <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol, MVMCoreActionDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, MFTextFieldDelegate, ButtonDelegateProtocol, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MVMCoreUIDetailViewProtocol, MoleculeDelegateProtocol>
|
||||
@interface MFViewController : UIViewController <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol, MVMCoreActionDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, MFTextFieldDelegate, ButtonDelegateProtocol, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MVMCoreUIDetailViewProtocol>
|
||||
|
||||
// Stores the load object that this screen was loaded with.
|
||||
@property (nullable, strong, nonatomic) MVMCoreLoadObject *loadObject;
|
||||
|
||||
@ -97,6 +97,17 @@
|
||||
self.pageType = loadObject.pageType;
|
||||
self.loadObject = loadObject;
|
||||
|
||||
NSError *parseError = nil;
|
||||
[self parsePageJSONAndReturnError:&parseError];
|
||||
if (parseError) {
|
||||
if (error) {
|
||||
MVMCoreErrorObject *errorObject = [MVMCoreErrorObject createErrorObjectForNSError:parseError location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
|
||||
errorObject.messageToDisplay = [MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess];
|
||||
*error = errorObject;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verifies all modules needed are loaded.
|
||||
return [MFViewController verifyRequiredModulesLoadedForLoadObject:loadObject error:error];
|
||||
}
|
||||
@ -249,6 +260,8 @@
|
||||
|
||||
- (BOOL)newPageLoaded:(nonnull NSDictionary *)page {
|
||||
self.loadObject.pageJSON = page;
|
||||
NSError *parseError = nil;
|
||||
[self parsePageJSONAndReturnError:&parseError];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@ -832,15 +845,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - MoleculeDelegateProtocol
|
||||
|
||||
- (NSDictionary *)getModuleWithName:(NSString *)name {
|
||||
if (!name) {
|
||||
return nil;
|
||||
}
|
||||
return [self.loadObject.modulesJSON dict:name];
|
||||
}
|
||||
|
||||
#pragma mark - adobe analytics
|
||||
|
||||
- (nullable NSArray <NSDictionary *> *)additionalActionsToTrackWithMainActionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData {
|
||||
|
||||
210
MVMCoreUI/Categories/UIColor+Extension.swift
Normal file
210
MVMCoreUI/Categories/UIColor+Extension.swift
Normal file
@ -0,0 +1,210 @@
|
||||
//
|
||||
// UIColor+Extension.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 10/24/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
import UIKit
|
||||
|
||||
public typealias ColorHexTuple = (uiColor: UIColor, hex: String)
|
||||
|
||||
extension UIColor {
|
||||
|
||||
/// Dictionary to access brand approved colors by name.
|
||||
public static let names: [String: ColorHexTuple] = ["black": (.mvmBlack, "#000000"),
|
||||
"white": (.mvmWhite, "#FFFFFF"),
|
||||
"red": (.mvmRed, "#D52B1E"),
|
||||
"orange": (.mvmOrange, "#CC4D0F"),
|
||||
"green": (.mvmGreen, "#008631"),
|
||||
"blue": (.mvmBlue, "#007AB8"),
|
||||
"blueGradient": (.mvmBlueGradient, "#007AB8"),
|
||||
"yellow": (.mvmYellow, "#FFBC3D"),
|
||||
"coolGray1": (.mvmCoolGray1, "#F6F6F6"),
|
||||
"coolGray3": (.mvmCoolGray3, "#D8DADA"),
|
||||
"coolGray6": (.mvmCoolGray6, "#747676"),
|
||||
"vzupGold": (.vzupGold, "#B89B56"),
|
||||
"vzupYellow1": (.vzupYellow1, "#F9D542"),
|
||||
"vzupYellow2": (.vzupYellow2, "#F4CA53"),
|
||||
"vzupYellow3": (.vzupYellow3, "#CC9B2D")]
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Brand
|
||||
//--------------------------------------------------
|
||||
|
||||
/// HEX: #000000
|
||||
public static let mvmBlack = UIColor.black
|
||||
|
||||
/// HEX: #FFFFFF
|
||||
public static let mvmWhite = UIColor.white
|
||||
|
||||
/// HEX: #D52B1E
|
||||
public static let mvmRed = UIColor.color8Bits(red: 213, green: 43, blue: 30)
|
||||
|
||||
/// HEX: #CC4D0F
|
||||
public static let mvmOrange = UIColor.color8Bits(red: 204, green: 77, blue: 15)
|
||||
|
||||
/// HEX: #008631
|
||||
public static let mvmGreen = UIColor.color8Bits(red: 0, green: 134, blue: 49)
|
||||
|
||||
/// HEX: #007AB8
|
||||
public static let mvmBlue = UIColor.color8Bits(red: 0, green: 122, blue: 184)
|
||||
|
||||
/// HEX: #007AB8
|
||||
public static let mvmBlueGradient = UIColor.color8Bits(red: 0, green: 122, blue: 184)
|
||||
|
||||
/// HEX: #FFBC3D
|
||||
public static let mvmYellow = UIColor.color8Bits(red: 255, green: 188, blue: 61)
|
||||
|
||||
/// HEX: #F6F6F6
|
||||
public static let mvmCoolGray1 = UIColor.grayscale(rgb: 246)
|
||||
|
||||
/// HEX: #D8DADA
|
||||
public static let mvmCoolGray3 = UIColor.color8Bits(red: 216, green: 218, blue: 218)
|
||||
|
||||
/// HEX: #747676
|
||||
public static let mvmCoolGray6 = UIColor.color8Bits(red: 116, green: 118, blue: 118)
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - VZ UP Brand
|
||||
//--------------------------------------------------
|
||||
|
||||
/// HEX: #B89B56
|
||||
public static let vzupGold = UIColor.color8Bits(red: 184, green: 155, blue: 68)
|
||||
|
||||
/// HEX: #F9D542
|
||||
public static let vzupYellow1 = UIColor.color8Bits(red: 249, green: 213, blue: 66)
|
||||
|
||||
/// HEX: #F4CA53
|
||||
public static let vzupYellow2 = UIColor.color8Bits(red: 244, green: 202, blue: 83)
|
||||
|
||||
/// HEX: #CC9B2D
|
||||
public static let vzupYellow3 = UIColor.color8Bits(red: 204, green: 155, blue: 45)
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Functions
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Convenience to get a grayscale UIColor where the same value is used for red, green, and blue.
|
||||
public class func grayscale(rgb: Int, alpha: CGFloat = 1.0) -> UIColor {
|
||||
|
||||
let grayscale = CGFloat(rgb) / 255.0
|
||||
return UIColor(red: grayscale, green: grayscale, blue: grayscale, alpha: alpha)
|
||||
}
|
||||
|
||||
/// Convenience to get a UIColor.
|
||||
public class func color8Bits(red: Int, green: Int, blue: Int, alpha: CGFloat = 1.0) -> UIColor {
|
||||
|
||||
return UIColor(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: alpha)
|
||||
}
|
||||
|
||||
/// Convenience to get a UIColor.
|
||||
public class func color8Bits(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat = 1.0) -> UIColor {
|
||||
|
||||
return UIColor(red: red / 255.0, green: green / 255.0, blue: blue / 255.0, alpha: alpha)
|
||||
}
|
||||
|
||||
/// Gets UIColor via an 8 digit hex string.
|
||||
public class func getColorBy(hex: String) -> UIColor {
|
||||
|
||||
var hexint: UInt64 = 0
|
||||
|
||||
let scanner = Scanner(string: hex)
|
||||
scanner.charactersToBeSkipped = CharacterSet(charactersIn: "#")
|
||||
scanner.scanHexInt64(&hexint)
|
||||
|
||||
return UIColor(red: (CGFloat((hexint & 0xFF0000) >> 16)) / 255,
|
||||
green: (CGFloat((hexint & 0xFF00) >> 8)) / 255,
|
||||
blue: (CGFloat(hexint & 0xFF)) / 255,
|
||||
alpha: 1)
|
||||
}
|
||||
|
||||
/// Gets UIColor via an 8 digit hex string. The last two being the alpha channel.
|
||||
public class func getColorWithTransparencyBy(hex: String) -> UIColor {
|
||||
|
||||
var hexint: UInt64 = 0
|
||||
|
||||
let scanner = Scanner(string: hex)
|
||||
scanner.charactersToBeSkipped = CharacterSet(charactersIn: "#")
|
||||
scanner.scanHexInt64(&hexint)
|
||||
|
||||
return UIColor(red: (CGFloat((hexint & 0xFF000000) >> 24)) / 255,
|
||||
green: (CGFloat((hexint & 0xFF0000) >> 16)) / 255,
|
||||
blue: (CGFloat((hexint & 0xFF00) >> 8)) / 255,
|
||||
alpha: (CGFloat(hexint & 0xFF)) / 255)
|
||||
}
|
||||
|
||||
public class func gradientColor(_ color: UIColor?) -> UIColor {
|
||||
|
||||
var h: CGFloat = 0
|
||||
var s: CGFloat = 0
|
||||
var b: CGFloat = 0
|
||||
var a: CGFloat = 0
|
||||
|
||||
if color?.getHue(&h, saturation: &s, brightness: &b, alpha: &a) ?? false {
|
||||
return UIColor(hue: h, saturation: max(s - 0.17, 0.0), brightness: min(b - 0.03, 1.0), alpha: a)
|
||||
}
|
||||
|
||||
return .white
|
||||
}
|
||||
|
||||
public class func setBackgroundColor(forNavigationBar color: UIColor, navigationBar: UINavigationBar, transparent: Bool) {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
let view = UIView(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
|
||||
view.backgroundColor = color
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
|
||||
|
||||
if let context = UIGraphicsGetCurrentContext() {
|
||||
view.layer.render(in: context)
|
||||
}
|
||||
|
||||
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
if transparent {
|
||||
navigationBar.setBackgroundImage(UIImage(), for: .default)
|
||||
navigationBar.isTranslucent = false
|
||||
} else {
|
||||
navigationBar.setBackgroundImage(image, for: .default)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// - parameter color: The UIColor intended to retrieve its hex value.
|
||||
public class func hexString(for color: UIColor) -> String? {
|
||||
|
||||
guard let components = color.cgColor.components else { return nil }
|
||||
|
||||
if color.cgColor.numberOfComponents >= 3 {
|
||||
let r = Int(CGFloat(components[0]) * 255)
|
||||
let g = Int(CGFloat(components[1]) * 255)
|
||||
let b = Int(CGFloat(components[2]) * 255)
|
||||
|
||||
// If alpha of color is less than 1.0 then alpha hex is relevant.
|
||||
if color.cgColor.numberOfComponents == 4 && components[3] < 1.0 {
|
||||
let a = Int(CGFloat(components[3]) * 255)
|
||||
return String(format: "%02X%02X%02X%02X", r, g, b, a)
|
||||
}
|
||||
|
||||
return String(format: "%02X%02X%02X", r, g, b)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
public class func getColorAndHexFromName(_ name: String) -> ColorHexTuple? {
|
||||
|
||||
for row in names {
|
||||
if name == row.key {
|
||||
return row.value
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -29,7 +29,7 @@ import UIKit
|
||||
let navigationController = self.init()
|
||||
style(navigationController.navigationBar)
|
||||
navigationController.separatorView = Line(pinTo: navigationController.navigationBar, edge: .bottom, useMargin: false)
|
||||
navigationController.separatorView?.style = .standard
|
||||
navigationController.separatorView?.setStyle(.standard)
|
||||
MVMCoreUISession.sharedGlobal()?.navigationController = navigationController
|
||||
MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = navigationController
|
||||
MVMCoreNavigationHandler.shared()?.navigationController = navigationController
|
||||
|
||||
82
MVMCoreUI/Containers/Views/Container/Container.swift
Normal file
82
MVMCoreUI/Containers/Views/Container/Container.swift
Normal file
@ -0,0 +1,82 @@
|
||||
//
|
||||
// Container.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 12/11/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
open class Container: View, ContainerProtocol {
|
||||
public var view: UIView?
|
||||
let containerHelper = ContainerHelper()
|
||||
var containerModel: ContainerModelProtocol? {
|
||||
get { return model as? ContainerModelProtocol }
|
||||
}
|
||||
|
||||
// MARK:- ModelMoleculeViewProtocol
|
||||
override open func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
guard let containerModel = model as? ContainerModelProtocol else { return }
|
||||
containerHelper.set(with: containerModel, for: view as? MVMCoreUIViewConstrainingProtocol)
|
||||
}
|
||||
|
||||
// MARK:- ContainerProtocol
|
||||
public func alignHorizontal(_ alignment: UIStackView.Alignment) {
|
||||
containerHelper.alignHorizontal(alignment)
|
||||
}
|
||||
|
||||
public func alignVertical(_ alignment: UIStackView.Alignment) {
|
||||
containerHelper.alignVertical(alignment)
|
||||
}
|
||||
|
||||
public func constrainView(_ view: UIView) {
|
||||
containerHelper.constrainView(view)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreViewProtocol
|
||||
public extension Container {
|
||||
override func updateView(_ size: CGFloat) {
|
||||
super.updateView(size)
|
||||
(view as? MVMCoreViewProtocol)?.updateView(size)
|
||||
containerHelper.updateViewMargins(self, model: containerModel, size: size)
|
||||
}
|
||||
|
||||
/// Will be called only once.
|
||||
override func setupView() {
|
||||
super.setupView()
|
||||
backgroundColor = .clear
|
||||
}
|
||||
|
||||
func addAndContain(_ view: UIView) {
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(view)
|
||||
containerHelper.constrainView(view)
|
||||
self.view = view
|
||||
}
|
||||
|
||||
convenience init(andContain view: UIView) {
|
||||
self.init()
|
||||
addAndContain(view)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
public extension Container {
|
||||
override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
guard let view = view else { return }
|
||||
containerHelper.set(with: json, for: view)
|
||||
}
|
||||
|
||||
override func reset() {
|
||||
super.reset()
|
||||
(view as? MVMCoreUIMoleculeViewProtocol)?.reset?()
|
||||
}
|
||||
|
||||
override func setAsMolecule() {
|
||||
(view as? MVMCoreUIMoleculeViewProtocol)?.setAsMolecule?()
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,13 @@
|
||||
//
|
||||
// Container.swift
|
||||
// ContainerHelper.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 12/11/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
// Created by Scott Pfeil on 1/21/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
// A Helper that consolidates some common container logic
|
||||
|
||||
import UIKit
|
||||
|
||||
public protocol ContainerModelProtocol {
|
||||
var horizontalAlignment: UIStackView.Alignment? { get set }
|
||||
var verticalAlignment: UIStackView.Alignment? { get set }
|
||||
var useHorizontalMargins: Bool? { get set }
|
||||
var useVerticalMargins: Bool? { get set }
|
||||
}
|
||||
import Foundation
|
||||
|
||||
public class ContainerHelper: NSObject {
|
||||
var leftConstraint: NSLayoutConstraint?
|
||||
@ -145,15 +139,6 @@ public class ContainerHelper: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
func set(with model: ContainerModelProtocol) {
|
||||
if let horizontalAlignment = model.horizontalAlignment {
|
||||
alignHorizontal(horizontalAlignment)
|
||||
}
|
||||
if let verticalAlignment = model.verticalAlignment {
|
||||
alignVertical(verticalAlignment)
|
||||
}
|
||||
}
|
||||
|
||||
static func getAlignment(for string: String) -> UIStackView.Alignment? {
|
||||
switch string {
|
||||
case "leading":
|
||||
@ -169,6 +154,34 @@ public class ContainerHelper: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
static func getAlignmentString(for alignment: UIStackView.Alignment?) -> String? {
|
||||
switch alignment {
|
||||
case .leading:
|
||||
return "leading"
|
||||
case .trailing:
|
||||
return "trailing"
|
||||
case .center:
|
||||
return "center"
|
||||
case .fill:
|
||||
return "fill"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateViewMargins(_ view: UIView, model: ContainerModelProtocol?, size: CGFloat) {
|
||||
MFStyler.setMarginsFor(view, size: size, defaultHorizontal: model?.useHorizontalMargins ?? false, top: (model?.useVerticalMargins ?? false) ? (model?.topMarginPadding ?? 0) : 0, bottom: (model?.useVerticalMargins ?? false) ? (model?.bottomMarginPadding ?? 0) : 0)
|
||||
}
|
||||
|
||||
func set(with model: ContainerModelProtocol, for contained: MVMCoreUIViewConstrainingProtocol?) {
|
||||
if let horizontalAlignment = model.horizontalAlignment ?? contained?.horizontalAlignment?() {
|
||||
alignHorizontal(horizontalAlignment)
|
||||
}
|
||||
if let verticalAlignment = model.verticalAlignment ?? contained?.verticalAlignment?() {
|
||||
alignVertical(verticalAlignment)
|
||||
}
|
||||
}
|
||||
|
||||
func set(with JSON: [AnyHashable: Any]?, for contained: UIView) {
|
||||
if let horizontalAlignmentString = JSON?.optionalStringForKey("horizontalAlignment"), let alignment = ContainerHelper.getAlignment(for: horizontalAlignmentString) ?? (contained as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() {
|
||||
alignHorizontal(alignment)
|
||||
@ -183,57 +196,3 @@ public class ContainerHelper: NSObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class Container: View {
|
||||
var model: ContainerModelProtocol?
|
||||
var view: UIView?
|
||||
let containerHelper = ContainerHelper()
|
||||
|
||||
var topMarginPadding: CGFloat = 0
|
||||
var bottomMarginPadding: CGFloat = 0
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreViewProtocol
|
||||
public extension Container {
|
||||
override func updateView(_ size: CGFloat) {
|
||||
super.updateView(size)
|
||||
(view as? MVMCoreViewProtocol)?.updateView(size)
|
||||
MFStyler.setMarginsFor(self, size: size, defaultHorizontal: model?.useHorizontalMargins ?? true, top: model?.useHorizontalMargins ?? true ? topMarginPadding : 0, bottom: model?.useHorizontalMargins ?? true ? bottomMarginPadding : 0)
|
||||
}
|
||||
|
||||
/// Will be called only once.
|
||||
override func setupView() {
|
||||
super.setupView()
|
||||
backgroundColor = .clear
|
||||
}
|
||||
|
||||
func addAndContain(_ view: UIView) {
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(view)
|
||||
containerHelper.constrainView(view)
|
||||
self.view = view
|
||||
}
|
||||
|
||||
convenience init(andContain view: UIView) {
|
||||
self.init()
|
||||
addAndContain(view)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
public extension Container {
|
||||
override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
guard let view = view else { return }
|
||||
containerHelper.set(with: json, for: view)
|
||||
}
|
||||
|
||||
override func reset() {
|
||||
super.reset()
|
||||
(view as? MVMCoreUIMoleculeViewProtocol)?.reset?()
|
||||
}
|
||||
|
||||
func setAsMolecule() {
|
||||
(view as? MVMCoreUIMoleculeViewProtocol)?.setAsMolecule?()
|
||||
}
|
||||
}
|
||||
54
MVMCoreUI/Containers/Views/Container/ContainerModel.swift
Normal file
54
MVMCoreUI/Containers/Views/Container/ContainerModel.swift
Normal file
@ -0,0 +1,54 @@
|
||||
//
|
||||
// ContainerModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Suresh, Kamlesh on 12/4/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class ContainerModel: ContainerModelProtocol, Codable {
|
||||
public var horizontalAlignment: UIStackView.Alignment?
|
||||
public var verticalAlignment: UIStackView.Alignment?
|
||||
public var useHorizontalMargins: Bool?
|
||||
|
||||
public var useVerticalMargins: Bool?
|
||||
public var topMarginPadding: CGFloat?
|
||||
public var bottomMarginPadding: CGFloat?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case verticalAlignment
|
||||
case horizontalAlignment
|
||||
case useHorizontalMargins
|
||||
case useVerticalMargins
|
||||
case topMarginPadding
|
||||
case bottomMarginPadding
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
if let verticalAlignmentString = try typeContainer.decodeIfPresent(String.self, forKey: .verticalAlignment) {
|
||||
verticalAlignment = ContainerHelper.getAlignment(for: verticalAlignmentString)
|
||||
}
|
||||
if let horizontalAlignmentString = try typeContainer.decodeIfPresent(String.self, forKey: .horizontalAlignment) {
|
||||
horizontalAlignment = ContainerHelper.getAlignment(for: horizontalAlignmentString)
|
||||
}
|
||||
useHorizontalMargins = try typeContainer.decodeIfPresent(Bool.self, forKey: .useHorizontalMargins)
|
||||
useVerticalMargins = try typeContainer.decodeIfPresent(Bool.self, forKey: .useVerticalMargins)
|
||||
topMarginPadding = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .topMarginPadding)
|
||||
bottomMarginPadding = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .bottomMarginPadding)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(ContainerHelper.getAlignmentString(for: verticalAlignment), forKey: .verticalAlignment)
|
||||
try container.encodeIfPresent(ContainerHelper.getAlignmentString(for: horizontalAlignment), forKey: .horizontalAlignment)
|
||||
try container.encodeIfPresent(useHorizontalMargins, forKey: .useHorizontalMargins)
|
||||
try container.encodeIfPresent(useVerticalMargins, forKey: .useVerticalMargins)
|
||||
try container.encodeIfPresent(topMarginPadding, forKey: .topMarginPadding)
|
||||
try container.encodeIfPresent(bottomMarginPadding, forKey: .bottomMarginPadding)
|
||||
}
|
||||
}
|
||||
16
MVMCoreUI/Containers/Views/Container/ContainerProtocol.swift
Normal file
16
MVMCoreUI/Containers/Views/Container/ContainerProtocol.swift
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// ContainerProtocol.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 1/16/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol ContainerProtocol {
|
||||
var view: UIView? { get set }
|
||||
func alignHorizontal(_ alignment: UIStackView.Alignment)
|
||||
func alignVertical(_ alignment: UIStackView.Alignment)
|
||||
func constrainView(_ view: UIView)
|
||||
}
|
||||
@ -118,7 +118,7 @@ import UIKit
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Holds reference to delegateObject to inform molecular tableView of an update.
|
||||
weak var delegateObject: MVMCoreUIDelegateObject?
|
||||
var delegateObject: MVMCoreUIDelegateObject?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Lifecycle
|
||||
@ -266,14 +266,22 @@ import UIKit
|
||||
let size: CGFloat = bottomBarSize ?? (showError ? 4 : 1)
|
||||
bottomBar?.frame = CGRect(x: 0, y: bounds.height - size, width: bounds.width, height: size)
|
||||
|
||||
delegateObject?.moleculeDelegate?.moleculeLayoutUpdated?(self)
|
||||
delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self)
|
||||
setNeedsDisplay()
|
||||
layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
self.delegateObject = delegateObject
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- MVMCoreUIMoleculeViewProtocol
|
||||
extension EntryFieldContainer {
|
||||
|
||||
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
69
MVMCoreUI/Containers/Views/MoleculeContainer.swift
Normal file
69
MVMCoreUI/Containers/Views/MoleculeContainer.swift
Normal file
@ -0,0 +1,69 @@
|
||||
//
|
||||
// MoleculeContainer.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 12/12/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
open class MoleculeContainer: Container {
|
||||
|
||||
/// Can be overriden to change how the molecule is added to the hierarchy.
|
||||
public func addMolecule(_ molecule: UIView) {
|
||||
addAndContain(molecule)
|
||||
}
|
||||
|
||||
override public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
|
||||
guard let moleculeJSON = json?.optionalDictionaryForKey(KeyMolecule) else {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
return
|
||||
}
|
||||
if view == nil {
|
||||
if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(forJSON: moleculeJSON, delegateObject: delegateObject, constrainIfNeeded: false) {
|
||||
addAndContain(molecule)
|
||||
}
|
||||
} else {
|
||||
(view as? MVMCoreUIMoleculeViewProtocol)?.setWithJSON?(moleculeJSON, delegateObject: delegateObject, additionalData: additionalData)
|
||||
}
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
}
|
||||
|
||||
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||
if let casteModel = model as? MoleculeContainerModel {
|
||||
if view != nil {
|
||||
(view as? ModelMoleculeViewProtocol)?.setWithModel(casteModel.molecule, delegateObject, additionalData)
|
||||
} else {
|
||||
if let molecule = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(casteModel.molecule, delegateObject) {
|
||||
addMolecule(molecule)
|
||||
}
|
||||
}
|
||||
}
|
||||
super.setWithModel(model, delegateObject, additionalData)
|
||||
}
|
||||
|
||||
public override static func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
|
||||
guard let containerModel = model as? MoleculeContainerModel,
|
||||
let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(containerModel.molecule) as? ModelMoleculeViewProtocol.Type,
|
||||
let moleculeName = moleculeClass.nameForReuse(containerModel.molecule, delegateObject) else {
|
||||
return "\(model?.moleculeName ?? "moleculeContainer")<>"
|
||||
}
|
||||
return "\(model?.moleculeName ?? "moleculeContainer")<\(moleculeName)>"
|
||||
}
|
||||
|
||||
public override class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
|
||||
guard let containerModel = molecule as? MoleculeContainerModel else { return 0 }
|
||||
guard let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(containerModel.molecule) as? ModelMoleculeViewProtocol.Type,
|
||||
let moleculeHeight = moleculeClass.estimatedHeight(forRow: containerModel.molecule, delegateObject: delegateObject) else {
|
||||
return (containerModel.topMarginPadding ?? 0) + (containerModel.bottomMarginPadding ?? 0)
|
||||
}
|
||||
return moleculeHeight + (containerModel.topMarginPadding ?? 0) + (containerModel.bottomMarginPadding ?? 0)
|
||||
}
|
||||
|
||||
public override class func requiredModules(_ molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
|
||||
guard let containerModel = molecule as? MoleculeContainerModel,
|
||||
let moleculeClass = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(containerModel.molecule) as? ModelMoleculeViewProtocol.Type else { return nil }
|
||||
return moleculeClass.requiredModules(containerModel.molecule, delegateObject: delegateObject, error: error)
|
||||
}
|
||||
}
|
||||
34
MVMCoreUI/Containers/Views/MoleculeContainerModel.swift
Normal file
34
MVMCoreUI/Containers/Views/MoleculeContainerModel.swift
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// MoleculeContainerModel.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 1/6/20.
|
||||
// Copyright © 2020 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MoleculeContainerModel: ContainerModel {
|
||||
public var molecule: MoleculeModelProtocol
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case molecule
|
||||
}
|
||||
|
||||
public init(with moleculeModel: MoleculeModelProtocol) {
|
||||
molecule = moleculeModel
|
||||
super.init()
|
||||
}
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||
molecule = try typeContainer.decodeMolecule(codingKey: .molecule)
|
||||
try super.init(from: decoder)
|
||||
}
|
||||
|
||||
public override func encode(to encoder: Encoder) throws {
|
||||
try super.encode(to: encoder)
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeModel(molecule, forKey: .molecule)
|
||||
}
|
||||
}
|
||||
123
MVMCoreUI/CustomPrimitives/Color.swift
Normal file
123
MVMCoreUI/CustomPrimitives/Color.swift
Normal file
@ -0,0 +1,123 @@
|
||||
//
|
||||
// Color.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 11/26/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
/*
|
||||
UIColor is not supported by Codable. This Color class
|
||||
effectively turns UIColor into a primitive class like
|
||||
Int and String and can be used the same.
|
||||
*/
|
||||
public final class Color: Codable {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
public let uiColor: UIColor
|
||||
|
||||
public var cgColor: CGColor {
|
||||
return uiColor.cgColor
|
||||
}
|
||||
|
||||
public private(set) var hex: String = ""
|
||||
public private(set) var name: String = ""
|
||||
|
||||
// Color metadata 🎨
|
||||
public private(set) var red: CGFloat = 0
|
||||
public private(set) var green: CGFloat = 0
|
||||
public private(set) var blue: CGFloat = 0
|
||||
public private(set) var alpha: CGFloat = 1
|
||||
|
||||
public var hexWithHash: String {
|
||||
return "#" + hex
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Error
|
||||
//--------------------------------------------------
|
||||
|
||||
enum ColorError: Error {
|
||||
case badName(reason: String)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Class Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
init(uiColor: UIColor) {
|
||||
self.uiColor = uiColor
|
||||
hex = UIColor.hexString(for: uiColor) ?? ""
|
||||
determineRGBA()
|
||||
}
|
||||
|
||||
init?(name: String) {
|
||||
guard let colorTuple = UIColor.getColorAndHexFromName(name) else { return nil }
|
||||
self.uiColor = colorTuple.uiColor
|
||||
self.hex = colorTuple.hex
|
||||
determineRGBA()
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Codable Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
let colorString = try container.decode(String.self)
|
||||
|
||||
if colorString.hasPrefix("#") {
|
||||
hex = colorString.replacingOccurrences(of: "#", with: "")
|
||||
} else {
|
||||
guard let hex = UIColor.getColorAndHexFromName(colorString)?.hex else { throw ColorError.badName(reason: "Check the spelling of your color.") }
|
||||
self.hex = hex.replacingOccurrences(of: "#", with: "")
|
||||
name = colorString
|
||||
}
|
||||
|
||||
if hex.count == 8 {
|
||||
uiColor = UIColor.getColorWithTransparencyBy(hex: hex)
|
||||
} else {
|
||||
uiColor = UIColor.getColorBy(hex: hex)
|
||||
}
|
||||
|
||||
determineRGBA()
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
try container.encode(hexWithHash)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
public func convertHexToFloat(start: String.Index, end: String.Index) -> CGFloat {
|
||||
|
||||
return CGFloat(Int(hex[start..<end], radix: 16) ?? 0)
|
||||
}
|
||||
|
||||
private func determineRGBA() {
|
||||
|
||||
guard !hex.isEmpty else { return }
|
||||
|
||||
let redStart = hex.startIndex
|
||||
let redEnd = hex.index(redStart, offsetBy: 2)
|
||||
red = convertHexToFloat(start: redStart, end: redEnd)
|
||||
|
||||
let greenEnd = hex.index(redEnd, offsetBy: 2)
|
||||
green = convertHexToFloat(start: redEnd, end: greenEnd)
|
||||
|
||||
let blueEnd = hex.index(greenEnd, offsetBy: 2)
|
||||
blue = convertHexToFloat(start: greenEnd, end: blueEnd)
|
||||
|
||||
if hex.count == 8 {
|
||||
let alphaEnd = hex.index(blueEnd, offsetBy: 2)
|
||||
alpha = convertHexToFloat(start: blueEnd, end: alphaEnd)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,7 @@
|
||||
@interface MFView : UIView <MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol>
|
||||
|
||||
@property (nullable, nonatomic, strong) NSDictionary *json;
|
||||
@property (nullable, nonatomic, strong) id model;
|
||||
|
||||
// Called in the initialization functions. Can setup ui here.
|
||||
- (void)setupView;
|
||||
|
||||
@ -10,14 +10,15 @@
|
||||
@import MVMCore.MVMCoreViewProtocol;
|
||||
@class MVMCoreUIDelegateObject;
|
||||
@class MVMCoreErrorObject;
|
||||
@class MoleculeModelProtocol;
|
||||
|
||||
@protocol MVMCoreUIMoleculeViewProtocol <NSObject, MVMCoreViewProtocol>
|
||||
|
||||
@optional
|
||||
|
||||
/// Sets up the ui based on the json
|
||||
- (void)setWithJSON:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData;
|
||||
|
||||
@optional
|
||||
|
||||
/// Called after init to provide an early setter for any molecule specific logic
|
||||
- (void)setAsMolecule;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user