Merge branch 'feature/coding' into 'develop'

Feature/coding

See merge request BPHV_MIPS/mvm_core_ui!197
This commit is contained in:
Suresh, Kamlesh Jain 2020-01-24 17:11:36 -05:00
commit fddd6640e4
217 changed files with 7594 additions and 1713 deletions

File diff suppressed because it is too large Load Diff

View 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)
}
}

View File

@ -8,7 +8,8 @@
// //
open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol { open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, ModelMoleculeViewProtocol {
//------------------------------------------------------ //------------------------------------------------------
// MARK: - Constants // 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: 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 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 contentHorizontalAlignment = .left
//set correct color after layout //set correct color after layout
@ -107,6 +110,7 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI
@objc open func setAsMolecule() { @objc open func setAsMolecule() {
backgroundColor = .clear backgroundColor = .clear
translatesAutoresizingMaskIntoConstraints = false
setTitleColor(enabledColor, for: .normal) setTitleColor(enabledColor, for: .normal)
setTitleColor(disabledColor, for: .disabled) 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 { public func needsToBeConstrained() -> Bool {
return true return true
} }

View 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)
}
}

View 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
}
}

View 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)
}
}

View 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)
}
}

View File

@ -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)
}
}

View File

@ -68,6 +68,14 @@ import UIKit
container.trailingAnchor.constraint(equalTo: dropDownCaretView.trailingAnchor, constant: 16).isActive = true container.trailingAnchor.constraint(equalTo: dropDownCaretView.trailingAnchor, constant: 16).isActive = true
dropDownCaretView.centerYAnchor.constraint(equalTo: container.centerYAnchor).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 // MARK: - MVMCoreUIMoleculeViewProtocol

View 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)
}
}

View File

@ -22,7 +22,20 @@ import UIKit
return calendar 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 // MARK: - Initializers
@ -83,7 +96,7 @@ import UIKit
if calendar.isDate(date, inSameDayAs: Date()) { if calendar.isDate(date, inSameDayAs: Date()) {
text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string") text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string")
} else { } else {
text = dateFormatter().string(from: date) text = dateFormatter.string(from: date)
} }
} }
@ -98,19 +111,12 @@ import UIKit
setTextWith(date: datePicker?.date) 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() guard let model = model as? DateDropdownEntryFieldModel else { return }
formatter.dateStyle = .medium
formatter.timeZone = NSTimeZone.system
formatter.locale = .current
formatter.formatterBehavior = .default
if let dateFormat = dateFormat { dateFormat = model.dateFormat
formatter.dateFormat = dateFormat
}
return formatter
} }
} }

View 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)
}
}

View File

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

View 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)
}
}

View File

@ -119,6 +119,7 @@ import UIKit
feedbackLabel.text = newFeedback feedbackLabel.text = newFeedback
feedbackLabel.accessibilityElementsHidden = feedbackLabel.text?.isEmpty ?? true feedbackLabel.accessibilityElementsHidden = feedbackLabel.text?.isEmpty ?? true
entryFieldContainer.refreshUI() entryFieldContainer.refreshUI()
delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self)
} }
} }
@ -238,6 +239,31 @@ import UIKit
feedbackLabel.textColor = .black feedbackLabel.textColor = .black
entryFieldContainer.reset() 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 // MARK: - MVMCoreUIMoleculeViewProtocol

View 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)
}
}

View File

@ -21,7 +21,7 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
public var setInitialValueInTextField = true public var setInitialValueInTextField = true
/// Closure passed here will run as picker changes items. /// 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. /// Closure passed here will run upon dismissing the selection picker.
public var observeDropdownSelection: ((String)->())? public var observeDropdownSelection: ((String)->())?
@ -90,6 +90,19 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
observeDropdownSelection?(pickerData[pickerIndex]) 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 // 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? { @objc public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
guard !pickerData.isEmpty else { return nil }
return pickerData[row] return pickerData[row]
} }
@objc public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { @objc public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
guard !pickerData.isEmpty else { return }
observeDropdownChange?(text ?? "", pickerData[row])
text = pickerData[row] text = pickerData[row]
observeDropdownChange?(pickerData[row])
} }
} }

View 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)
}
}

View 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()
}
}
}

View 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"
}
}

View File

@ -270,6 +270,49 @@ import UIKit
resignFirstResponder() 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 // MARK: - MVMCoreUIMoleculeViewProtocol

View 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)
}
}

View 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?
}

View File

@ -178,29 +178,27 @@ open class CaretView: View {
//------------------------------------------------------ //------------------------------------------------------
// Default values for view. // Default values for view.
@objc open func setAsMolecule() { @objc open override func setAsMolecule() {
defaultState() defaultState()
} }
@objc open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: CaretViewModel.self) else { return }
setWithModel(model, delegateObject, additionalData)
guard let dictionary = json else { return } }
if let strokeColorHex = dictionary["strokeColor"] as? String { //MARK: - MVMCoreMoleculeViewProtocol
strokeColor = UIColor.mfGet(forHex: strokeColorHex) override public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithModel(model, delegateObject, additionalData)
guard let caretModel = model as? CaretViewModel else {
return
} }
strokeColor = caretModel.strokeColor.uiColor
if let isHidden = dictionary[KeyIsHidden] as? Bool { isHidden = caretModel.isHidden ?? false
self.isHidden = isHidden isOpaque = caretModel.isOpaque ?? false
}
if let lineWidthValue = caretModel.lineWidth {
if let isOpaque = dictionary[KeyIsOpaque] as? Bool { lineWidth = lineWidthValue
self.isOpaque = isOpaque
}
if let lineWidth = dictionary["lineWidth"] as? CGFloat {
self.lineWidth = lineWidth
} }
} }
} }

View 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)
}
}

View File

@ -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. /// Color of the check mark.
public var checkColor: UIColor = .black { public var checkColor: UIColor = .black {
didSet { didSet {
@ -107,7 +128,6 @@ import MVMCore
if !updateSelectionOnly { if !updateSelectionOnly {
layoutIfNeeded() layoutIfNeeded()
shapeLayer?.removeAllAnimations() shapeLayer?.removeAllAnimations()
updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated) updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated)
FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol) FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol)
updateAccessibilityLabel() updateAccessibilityLabel()
@ -154,16 +174,12 @@ import MVMCore
public convenience init(isChecked: Bool) { public convenience init(isChecked: Bool) {
self.init(frame: .zero) self.init(frame: .zero)
updateSelectionOnly = true checkAndBypassAnimations(selected: isChecked)
isSelected = isChecked
updateSelectionOnly = false
} }
public convenience init(checkedBackgroundColor: UIColor, unCheckedBackgroundColor: UIColor, isChecked: Bool = false) { public convenience init(checkedBackgroundColor: UIColor, unCheckedBackgroundColor: UIColor, isChecked: Bool = false) {
self.init(frame: .zero) self.init(frame: .zero)
updateSelectionOnly = true checkAndBypassAnimations(selected: isChecked)
isSelected = isChecked
updateSelectionOnly = false
self.checkedBackgroundColor = checkedBackgroundColor self.checkedBackgroundColor = checkedBackgroundColor
self.unCheckedBackgroundColor = unCheckedBackgroundColor self.unCheckedBackgroundColor = unCheckedBackgroundColor
} }
@ -177,8 +193,6 @@ import MVMCore
drawShapeLayer() drawShapeLayer()
layer.cornerRadius = isRound ? cornerRadiusValue : 0 layer.cornerRadius = isRound ? cornerRadiusValue : 0
layer.borderWidth = borderWidth
layer.borderColor = borderColor.cgColor
} }
open override func setupView() { open override func setupView() {
@ -228,7 +242,7 @@ import MVMCore
self.shapeLayer = shapeLayer self.shapeLayer = shapeLayer
shapeLayer.frame = bounds shapeLayer.frame = bounds
layer.addSublayer(shapeLayer) layer.addSublayer(shapeLayer)
shapeLayer.strokeColor = checkColor.cgColor shapeLayer.strokeColor = isEnabled ? checkColor.cgColor : disabledCheckColor.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.path = checkMarkPath() shapeLayer.path = checkMarkPath()
shapeLayer.lineJoin = .miter shapeLayer.lineJoin = .miter
@ -268,9 +282,7 @@ import MVMCore
DispatchQueue.main.async { DispatchQueue.main.async {
self.updateSelectionOnly = true self.checkAndBypassAnimations(selected: selected)
self.isSelected = selected
self.updateSelectionOnly = false
self.drawShapeLayer() self.drawShapeLayer()
self.shapeLayer?.removeAllAnimations() self.shapeLayer?.removeAllAnimations()
self.updateCheckboxUI(isSelected: selected, isAnimated: animated) 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) { private func setShapeLayerStrokeColor(_ color: UIColor) {
if let shapeLayer = shapeLayer { if let shapeLayer = shapeLayer {
@ -345,13 +340,19 @@ import MVMCore
widthConstraint?.isActive = isActive widthConstraint?.isActive = isActive
} }
private func checkAndBypassAnimations(selected: Bool) {
updateSelectionOnly = true
isSelected = selected
updateSelectionOnly = false
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - UITouch // MARK: - UITouch
//-------------------------------------------------- //--------------------------------------------------
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
sendActions(for: .touchUpInside) sendActions(for: .touchUpInside)
} }
override open func accessibilityActivate() -> Bool { override open func accessibilityActivate() -> Bool {
@ -370,7 +371,7 @@ import MVMCore
open override func reset() { open override func reset() {
super.reset() super.reset()
isEnabled(true) isEnabled = true
shapeLayer?.removeAllAnimations() shapeLayer?.removeAllAnimations()
shapeLayer?.removeFromSuperlayer() shapeLayer?.removeFromSuperlayer()
shapeLayer = nil shapeLayer = nil
@ -379,9 +380,7 @@ import MVMCore
borderWidth = 1.0 borderWidth = 1.0
checkColor = .black checkColor = .black
checkWidth = 2.0 checkWidth = 2.0
updateSelectionOnly = true checkAndBypassAnimations(selected: false)
isSelected = false
updateSelectionOnly = false
} }
open func setAsMolecule() { open func setAsMolecule() {
@ -424,16 +423,14 @@ import MVMCore
layer.borderWidth = borderWidth layer.borderWidth = borderWidth
} }
if let isChecked = dictionary["isChecked"] as? Bool, isChecked {
updateSelectionOnly = true
isSelected = isChecked
updateSelectionOnly = false
}
if let checkColorHex = dictionary["checkColor"] as? String { if let checkColorHex = dictionary["checkColor"] as? String {
checkColor = UIColor.mfGet(forHex: checkColorHex) checkColor = UIColor.mfGet(forHex: checkColorHex)
} }
if let isChecked = dictionary["isChecked"] as? Bool, isChecked {
checkAndBypassAnimations(selected: isChecked)
}
if let unCheckedBackgroundColorHex = dictionary["unCheckedBackgroundColor"] as? String { if let unCheckedBackgroundColorHex = dictionary["unCheckedBackgroundColor"] as? String {
unCheckedBackgroundColor = UIColor.mfGet(forHex: unCheckedBackgroundColorHex) unCheckedBackgroundColor = UIColor.mfGet(forHex: unCheckedBackgroundColorHex)
} }
@ -451,13 +448,57 @@ import MVMCore
} }
if let enabled = dictionary["isEnabled"] as? Bool { 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) } 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 // MARK:- FormValidationProtocol

View 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
}

View 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)
}
}

View File

@ -21,12 +21,6 @@
public var checkboxPosition: CheckboxPosition = .center public var checkboxPosition: CheckboxPosition = .center
public enum CheckboxPosition: String {
case center
case top
case bottom
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Constraints // MARK: - Constraints
//-------------------------------------------------- //--------------------------------------------------
@ -120,6 +114,17 @@
checkboxCenterYConstraint?.isActive = false 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 // MARK: - Molecular

View 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
}
}
}

View File

@ -15,7 +15,14 @@ open class DashLine: View {
// MARK: - Properties // 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 // MARK: - Initializer
@ -64,9 +71,10 @@ open class DashLine: View {
dashLayer.lineCap = .round dashLayer.lineCap = .round
dashLayer.lineDashPattern = [NSNumber(value: 2), NSNumber(value: 2)] dashLayer.lineDashPattern = [NSNumber(value: 2), NSNumber(value: 2)]
dashLayer.path = path.cgPath dashLayer.path = path.cgPath
dashLayer.strokeColor = dashColor?.cgColor ?? UIColor.mfLighterGray().cgColor dashLayer.strokeColor = dashModel?.dashColor.cgColor ?? dashColor?.cgColor
dashLayer.fillColor = UIColor.clear.cgColor dashLayer.fillColor = UIColor.clear.cgColor
dashLayer.backgroundColor = backgroundColor?.cgColor ?? UIColor.white.cgColor dashLayer.backgroundColor = backgroundColor?.cgColor ?? UIColor.white.cgColor
self.dashLayer = dashLayer
} }
//------------------------------------------------------ //------------------------------------------------------
@ -74,23 +82,27 @@ open class DashLine: View {
//------------------------------------------------------ //------------------------------------------------------
// Default values for view. // Default values for view.
@objc open func setAsMolecule() { @objc open override func reset() {
backgroundColor = .clear backgroundColor = .clear
isHidden = false isHidden = false
} }
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: DashLineModel.self) else { return }
setWithModel(model, delegateObject, additionalData)
// Configure class properties with JSON values }
guard let jsonDictionary = json else { return }
//MARK: - MVMCoreMoleculeViewProtocol
if let isHiddenValue = jsonDictionary[KeyIsHidden] as? Bool { 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 isHidden = isHiddenValue
} }
if let backgroundColor = dashLineModel.backgroundColor {
if let dashColorHex = jsonDictionary["dashColor"] as? String { dashLayer?.backgroundColor = backgroundColor.uiColor.cgColor
dashColor = UIColor.mfGet(forHex: dashColorHex)
} }
} }
} }

View 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)
}
}

View File

@ -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
}
}

View File

@ -8,113 +8,13 @@
import UIKit 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 { @objcMembers open class GraphView: View, MVMCoreUIViewConstrainingProtocol {
var heightConstraint: NSLayoutConstraint? var heightConstraint: NSLayoutConstraint?
var gradientLayer: CALayer? var gradientLayer: CALayer?
var graphObject: GraphObject? var graphModel: CircleProgressModel? {
return model as? CircleProgressModel
}
// MARK: setup // MARK: setup
open override func setupView() { open override func setupView() {
@ -125,13 +25,17 @@ public struct GraphObject {
heightConstraint?.isActive = true heightConstraint?.isActive = true
widthAnchor.constraint(equalTo: heightAnchor).isActive = true widthAnchor.constraint(equalTo: heightAnchor).isActive = true
} }
override open func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { override open func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) super.setWithModel(model, delegateObject, additionalData)
let object = GraphObject(json) guard let model = model as? CircleProgressModel else { return }
graphObject = object createGraphCircle(model)
createGraphCircle(object) rotationAnimation(model)
rotationAnimation(object) }
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 { class func getAngle(_ piValue: Double) -> Double {
@ -143,7 +47,7 @@ public struct GraphObject {
} }
// MARK: circle // MARK: circle
open func createGraphCircle(_ graphObject: GraphObject) { open func createGraphCircle(_ graphObject: CircleProgressModel) {
if let sublayers = layer.sublayers { if let sublayers = layer.sublayers {
for sublayer in sublayers { for sublayer in sublayers {
sublayer.removeAllAnimations() sublayer.removeAllAnimations()
@ -184,14 +88,14 @@ public struct GraphObject {
| | | | | |
------------- -------------
*/ */
func createGradientLayer(_ graphObject: GraphObject) -> CALayer { func createGradientLayer(_ graphObject: CircleProgressModel) -> CALayer {
let containLayer = CALayer() let containLayer = CALayer()
containLayer.frame = CGRect(x: 0, y: 0, width: graphObject.diameter, height: graphObject.diameter) containLayer.frame = CGRect(x: 0, y: 0, width: graphObject.diameter, height: graphObject.diameter)
let radius = graphObject.diameter / 2.0 let radius = graphObject.diameter / 2.0
//create graident layers //create graident layers
guard graphObject.colors.count > 1 else { guard graphObject.colors.count > 1 else {
containLayer.backgroundColor = graphObject.colors.first containLayer.backgroundColor = graphObject.colors.first?.uiColor.cgColor
return containLayer return containLayer
} }
var topGradientHeight : CGFloat = 0.0 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 number of colors is even, need to display gradient layer, otherwise make top layer as solid color layer
if graphObject.colors.count % 2 == 0 { if graphObject.colors.count % 2 == 0 {
leftColors.removeLast() 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 { } else {
topLayer.backgroundColor = leftColors.last topLayer.backgroundColor = leftColors.last?.uiColor.cgColor
} }
containLayer.addSublayer(topLayer) containLayer.addSublayer(topLayer)
@ -221,9 +127,11 @@ public struct GraphObject {
//count of graidentLayer.colors must be bigger than 1, otherwise set backgroundColor //count of graidentLayer.colors must be bigger than 1, otherwise set backgroundColor
if leftColors.count > 1 { if leftColors.count > 1 {
leftLayer.colors = Array(leftColors) leftLayer.colors = leftColors.map({ (color) -> CGColor in
return color.uiColor.cgColor
})
} else { } else {
leftLayer.backgroundColor = leftColors.first leftLayer.backgroundColor = leftColors.first?.uiColor.cgColor
} }
containLayer.addSublayer(leftLayer) containLayer.addSublayer(leftLayer)
@ -232,9 +140,11 @@ public struct GraphObject {
rightLayer.startPoint = CGPoint(x: 0, y: 0) rightLayer.startPoint = CGPoint(x: 0, y: 0)
rightLayer.endPoint = CGPoint(x: 0, y: 1) rightLayer.endPoint = CGPoint(x: 0, y: 1)
if rightColors.count > 1 { if rightColors.count > 1 {
rightLayer.colors = Array(rightColors) rightLayer.colors = rightColors.map({ (color) -> CGColor in
return color.uiColor.cgColor
})
} else { } else {
rightLayer.backgroundColor = rightColors.first rightLayer.backgroundColor = rightColors.first?.uiColor.cgColor
} }
containLayer.addSublayer(rightLayer) containLayer.addSublayer(rightLayer)
@ -246,7 +156,7 @@ public struct GraphObject {
} }
//MARK: Animation //MARK: Animation
func rotationAnimation(_ object: GraphObject) { func rotationAnimation(_ object: CircleProgressModel) {
MVMCoreDispatchUtility.performBlock(onMainThread:{ MVMCoreDispatchUtility.performBlock(onMainThread:{
let rotation = CABasicAnimation(keyPath: "transform.rotation") let rotation = CABasicAnimation(keyPath: "transform.rotation")
let animationHandler = GraphViewAnimationHandler.shared let animationHandler = GraphViewAnimationHandler.shared
@ -277,7 +187,7 @@ public struct GraphObject {
extension GraphView: CAAnimationDelegate { extension GraphView: CAAnimationDelegate {
public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if let object = graphObject { if let object = graphModel {
rotationAnimation(object) rotationAnimation(object)
} }
} }

View 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?
}

View File

@ -12,7 +12,8 @@ import MVMCore
public typealias ActionBlock = () -> () public typealias ActionBlock = () -> ()
@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol { @objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol, ModelMoleculeViewProtocol {
//------------------------------------------------------ //------------------------------------------------------
// MARK: - Properties // 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]?) { @objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) {
guard let label = label else { return } guard let label = label else { return }

View 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
}
}

View 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)
}
}

View 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)
}
}

View 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)
}
}

View 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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View 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)
}
}

View 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?
}

View File

@ -9,7 +9,7 @@
import Foundation import Foundation
@objcMembers open class LeftRightLabelView: ViewConstrainingView { @objcMembers open class LeftRightLabelView: View {
//------------------------------------------------------ //------------------------------------------------------
// MARK: - Outlets // MARK: - Outlets
//------------------------------------------------------ //------------------------------------------------------
@ -28,8 +28,8 @@ import Foundation
// MARK: - Initialization // MARK: - Initialization
//------------------------------------------------------ //------------------------------------------------------
public init() { public convenience init() {
super.init(frame: .zero) self.init(frame: .zero)
} }
public override init(frame: CGRect) { public override init(frame: CGRect) {
@ -176,4 +176,20 @@ import Foundation
constrainLeftLabel() 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()
}
}
} }

View File

@ -9,42 +9,35 @@
import UIKit import UIKit
@objcMembers open class Line: View { @objcMembers open class Line: View {
var lineModel: LineModel? {
get { return model as? LineModel }
}
public var heightConstraint: NSLayoutConstraint? public var heightConstraint: NSLayoutConstraint?
public enum Style: String, Codable { open func setStyle(_ style: LineModel.Style) {
case standard switch style {
case thin case .standard:
case medium heightConstraint?.constant = 1
case heavy backgroundColor = .mfSilver()
case none case .thin:
} heightConstraint?.constant = 1
backgroundColor = .black
public var style = Style.standard { case .medium:
didSet { heightConstraint?.constant = 2
switch style { backgroundColor = .black
case .standard: case .heavy:
heightConstraint?.constant = 1 heightConstraint?.constant = 4
backgroundColor = .mfSilver() backgroundColor = .black
case .thin: case .none:
heightConstraint?.constant = 1 heightConstraint?.constant = 0
backgroundColor = .black
case .medium:
heightConstraint?.constant = 2
backgroundColor = .black
case .heavy:
heightConstraint?.constant = 4
backgroundColor = .black
case .none:
heightConstraint?.constant = 0
}
} }
} }
// MARK: - Helpers // MARK: - Helpers
open func shouldBeVisible() -> Bool { open func shouldBeVisible() -> Bool {
guard let type = json?.optionalStringForKey(KeyType) else { return false } guard let type = lineModel?.type else { return false }
return type != "none" return type != .none
} }
public convenience init(pinTo view: UIView, edge: UIRectEdge, useMargin: Bool) { public convenience init(pinTo view: UIView, edge: UIRectEdge, useMargin: Bool) {
@ -56,26 +49,33 @@ import UIKit
// MARK: - MVMCoreViewProtocol // MARK: - MVMCoreViewProtocol
open override func setupView() { open override func setupView() {
super.setupView() super.setupView()
backgroundColor = .black
heightConstraint = heightAnchor.constraint(equalToConstant: 1) heightConstraint = heightAnchor.constraint(equalToConstant: 1)
heightConstraint?.isActive = true heightConstraint?.isActive = true
setStyle(.standard)
} }
// MARK: - MVMCoreUIMoleculeViewProtocol // MARK: - MVMCoreUIMoleculeViewProtocol
open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
// If no type, default to standard. // If no type, default to standard.
if let typeString = json?.optionalStringForKey(KeyType), let type = Style.init(rawValue: typeString) { if let typeString = json?.optionalStringForKey(KeyType), let type = LineModel.Style.init(rawValue: typeString) {
style = type setStyle(type)
} else { } else {
style = .standard setStyle(.standard)
} }
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) 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() { open override func reset() {
style = .standard setStyle(.standard)
} }
open func copyBackgroundColor() -> Bool { open func copyBackgroundColor() -> Bool {
@ -83,7 +83,7 @@ import UIKit
} }
public static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { 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 { switch style {
case .none: case .none:
return 0 return 0

View 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)
}
}

View File

@ -8,7 +8,7 @@
import UIKit import UIKit
@objcMembers open class MFLoadImageView: ViewConstrainingView { @objcMembers open class MFLoadImageView: ViewConstrainingView, ModelMoleculeViewProtocol {
public let loadingSpinner = MFLoadingSpinner(frame: .zero) public let loadingSpinner = MFLoadingSpinner(frame: .zero)
public let imageView = MFTransparentGIFView(frame: .zero) public let imageView = MFTransparentGIFView(frame: .zero)
public var addSizeConstraintsForAspectRatio = false 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 // MARK: - MVMCoreUIMoleculeViewProtocol functions
open override func setAsMolecule() { open override func setAsMolecule() {
addSizeConstraintsForAspectRatio = true addSizeConstraintsForAspectRatio = true
@ -264,7 +289,7 @@ import UIKit
self?.addConstraints(width: width, height: height, size: image?.size) self?.addConstraints(width: width, height: height, size: image?.size)
self?.loadingSpinnerHeightConstraint?.constant = 0 self?.loadingSpinnerHeightConstraint?.constant = 0
if layoutWillChange { if layoutWillChange {
self?.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated?(self!) self?.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self!)
} }
completionHandler(image,data,isFallbackImage) completionHandler(image,data,isFallbackImage)
})} })}

View 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)
}
}

View 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)
}
}
}
}

View File

@ -8,35 +8,13 @@
import UIKit 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 { @objcMembers open class MultiProgress: View {
var multiProgressModel: MultiProgressBarModel? {
get { return model as? MultiProgressBarModel }
}
///passing value to progressList creates corresponding progress bars ///passing value to progressList creates corresponding progress bars
var progressList: Array<ProgressBarObject>? { var progressList: Array<SingleProgressBarModel>? {
didSet { didSet {
for subview in subviews { for subview in subviews {
subview.removeFromSuperview() subview.removeFromSuperview()
@ -52,7 +30,7 @@ import UIKit
let view = UIView(frame: .zero) let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false view.translatesAutoresizingMaskIntoConstraints = false
addSubview(view) addSubview(view)
view.backgroundColor = progressObject.color view.backgroundColor = progressObject.progressColor.uiColor
view.widthAnchor.constraint(equalTo: widthAnchor, multiplier: progressObject.progress).isActive = true view.widthAnchor.constraint(equalTo: widthAnchor, multiplier: progressObject.progress).isActive = true
view.leadingAnchor.constraint(equalTo: previous?.trailingAnchor ?? leadingAnchor).isActive = true view.leadingAnchor.constraint(equalTo: previous?.trailingAnchor ?? leadingAnchor).isActive = true
previous = view previous = view
@ -70,6 +48,7 @@ import UIKit
} }
} }
} }
var thicknessConstraint: NSLayoutConstraint? var thicknessConstraint: NSLayoutConstraint?
let defaultHeight: CGFloat = 8 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() { open override func reset() {
super.reset() super.reset()
backgroundColor = .mfLightSilver() backgroundColor = .mfLightSilver()
progressList = nil progressList = nil
} }
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: MultiProgressBarModel.self) else { return }
thicknessConstraint?.constant = json?.optionalCGFloatForKey("thickness") ?? defaultHeight setWithModel(model, delegateObject, additionalData)
roundedRect = json?.optionalBoolForKey("roundedRect") ?? false
progressList = ProgressBarObject.getProgressBarObjectList(json?.optionalArrayForKey("progressList") as? [[AnyHashable: Any]])
} }
} }

View 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)
}
}

View File

@ -8,12 +8,13 @@
import Foundation import Foundation
@objcMembers open class ProgressBar: UIProgressView, MVMCoreUIMoleculeViewProtocol, MVMCoreViewProtocol { @objcMembers open class ProgressBar: UIProgressView, MVMCoreViewProtocol, ModelMoleculeViewProtocol, MVMCoreUIMoleculeViewProtocol {
var isRounded = false var progressBarModel: ProgressBarModel?
var thickness: CGFloat = 8.0 { var thickness: CGFloat = 8.0 {
willSet(newValue) { willSet(newValue) {
heightAnchor.constraint(equalToConstant: newValue).isActive = true heightAnchor.constraint(equalToConstant: newValue).isActive = true
if isRounded { if progressBarModel?.isRounded ?? false {
layer.cornerRadius = newValue/2.0 layer.cornerRadius = newValue/2.0
} else { } else {
progressViewStyle = .bar progressViewStyle = .bar
@ -40,34 +41,35 @@ import Foundation
public func setupView() { public func setupView() {
clipsToBounds = true clipsToBounds = true
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
reset() thickness = 8
progress = 0
progressTintColor = UIColor.mfCerulean()
trackTintColor = UIColor.mfLightSilver()
} }
public func updateView(_ size: CGFloat) { public func updateView(_ size: CGFloat) {
} }
// MARK: - MVMCoreUIMoleculeViewProtocol //MARK: - MVMCoreMoleculeViewProtocol
public func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
if let isRounded = json?.optionalBoolForKey("roundedRect") { guard let progressBarModel = model as? ProgressBarModel else {
self.isRounded = isRounded return
} }
if let thickness = json?.optionalCGFloatForKey("thickness") { thickness = progressBarModel.thickness ?? 8
self.thickness = thickness progress = Float((progressBarModel.percent)/100.0)
} progressTintColor = progressBarModel.progressColor.uiColor
// as? Float returns nil, apple defect. if let backgroundColor = progressBarModel.backgroundColor {
if let percentage = json?["percent"] as? CGFloat { trackTintColor = backgroundColor.uiColor
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)
} }
} }
// 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() { public func reset() {
isRounded = false
thickness = 8 thickness = 8
progress = 0 progress = 0
progressTintColor = UIColor.mfCerulean() progressTintColor = UIColor.mfCerulean()

View 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)
}
}

View File

@ -332,6 +332,20 @@ public typealias ActionBlockConfirmation = () -> (Bool)
layoutIfNeeded() 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 // MARK: - Accessibility
@ -389,7 +403,7 @@ extension Toggle {
changeStateNoAnimation(state) changeStateNoAnimation(state)
} }
if let actionMap = dictionary.optionalDictionaryForKey("actionMap") { if let actionMap = dictionary.optionalDictionaryForKey("action") {
didToggleAction = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } didToggleAction = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) }
} }
@ -410,7 +424,7 @@ extension Toggle {
return true return true
} }
public func alignment() -> UIStackView.Alignment { public func horizontalAlignment() -> UIStackView.Alignment {
return .trailing return .trailing
} }
} }

View 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)
}
}

View File

@ -6,26 +6,24 @@
// Copyright © 2019 Verizon Wireless. All rights reserved. // Copyright © 2019 Verizon Wireless. All rights reserved.
// //
public typealias ButtonBlock = (Button) -> () public typealias ButtonAction = (Button) -> ()
@objcMembers open class Button: UIButton, MFButtonProtocol, ModelMoleculeViewProtocol {
@objcMembers open class Button: UIButton, MFButtonProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
open var model: MoleculeModelProtocol?
public var json: [AnyHashable: Any]? open var actionModel: ActionModelProtocol?
public var actionMap: [AnyHashable: Any]?
private var initialSetupPerformed = false private var initialSetupPerformed = false
private var buttonBlock: ButtonBlock? private var buttonAction: ButtonAction?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Delegate // MARK: - Delegate
//-------------------------------------------------- //--------------------------------------------------
public weak var buttonDelegate: ButtonDelegateProtocol? open weak var buttonDelegate: ButtonDelegateProtocol?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Initializers // MARK: - Initializers
@ -50,6 +48,7 @@ public typealias ButtonBlock = (Button) -> ()
// MARK: - Setup // MARK: - Setup
//-------------------------------------------------- //--------------------------------------------------
/// Required to be called any init. Ensures setupView() only gets called once
public func initialSetup() { public func initialSetup() {
if !initialSetupPerformed { if !initialSetupPerformed {
@ -62,61 +61,58 @@ public typealias ButtonBlock = (Button) -> ()
// MARK: - Methods // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
public func addBlock( event: Event, _ buttonBlock: @escaping ButtonBlock) { /// Adds a block to be performed for the given event.
self.buttonBlock = buttonBlock open func addActionBlock(event: Event, _ buttonBlock: @escaping ButtonAction) {
addTarget(self, action: #selector(callBlock(_:)), for: event) self.buttonAction = buttonBlock
addTarget(self, action: #selector(callActionBlock(_:)), for: event)
} }
func callBlock(_ sender: Button) { @objc private func callActionBlock(_ sender: Button) {
buttonBlock?(self) buttonAction?(self)
} }
public func setWithActionMap(_ actionMap: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { open func set(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
self.actionMap = actionMap self.actionModel = actionModel
buttonDelegate = delegateObject?.buttonDelegate buttonDelegate = delegateObject?.buttonDelegate
addBlock(event: .touchUpInside) { [weak self] sender in addActionBlock(event: .touchUpInside) { [weak self] sender in
guard let self = self else { return } guard let self = self else { return }
if let data = try? actionModel.encode(using: JSONEncoder()),
if self.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { 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) 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]?) { // MARK:- ModelMoleculeViewProtocol
self.json = json open func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
self.model = model
guard let dictionary = json else { return } if let backgroundColor = model?.backgroundColor {
self.backgroundColor = backgroundColor.uiColor
if let backgroundColorString = dictionary[KeyBackgroundColor] as? String {
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
}
if let title = dictionary[KeyTitle] as? String {
setTitle(title, for: .normal)
} }
} }
public func reset() { open class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
backgroundColor = .clear 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 // MARK: - MVMCoreViewProtocol
extension Button: MVMCoreViewProtocol { extension Button: MVMCoreViewProtocol {
public func updateView(_ size: CGFloat) {} open func updateView(_ size: CGFloat) {}
/// Will be called only once. /// Will be called only once.
public func setupView() { open func setupView() {
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
insetsLayoutMarginsFromSafeArea = false insetsLayoutMarginsFromSafeArea = false
titleLabel?.numberOfLines = 0 titleLabel?.numberOfLines = 0
@ -124,9 +120,15 @@ extension Button: MVMCoreViewProtocol {
} }
} }
// MARK: - MVMCoreUIMoleculeViewProtocol
extension Button: MVMCoreUIMoleculeViewProtocol {
open func reset() {
backgroundColor = .clear
}
}
// MARK: AppleGuidelinesProtocol // MARK: AppleGuidelinesProtocol
extension Button: AppleGuidelinesProtocol { extension Button: AppleGuidelinesProtocol {
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return Self.acceptablyOutsideBounds(point: point, bounds: bounds) return Self.acceptablyOutsideBounds(point: point, bounds: bounds)
} }

View File

@ -8,13 +8,13 @@
import UIKit import UIKit
@objcMembers open class Control: UIControl { @objcMembers open class Control: UIControl, ModelMoleculeViewProtocol {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Properties // MARK: - Properties
//-------------------------------------------------- //--------------------------------------------------
open var json: [AnyHashable: Any]?
public var json: [AnyHashable: Any]? open var model: MoleculeModelProtocol?
private var initialSetupPerformed = false private var initialSetupPerformed = false
//-------------------------------------------------- //--------------------------------------------------
@ -41,19 +41,36 @@ import UIKit
//-------------------------------------------------- //--------------------------------------------------
public func initialSetup() { public func initialSetup() {
if !initialSetupPerformed { if !initialSetupPerformed {
initialSetupPerformed = true initialSetupPerformed = true
setupView() 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 // MARK: - AppleGuidelinesProtocol
extension Control: AppleGuidelinesProtocol { extension Control: AppleGuidelinesProtocol {
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return Self.acceptablyOutsideBounds(point: point, bounds: bounds) return Self.acceptablyOutsideBounds(point: point, bounds: bounds)
} }
} }
@ -61,10 +78,10 @@ extension Control: AppleGuidelinesProtocol {
// MARK: - MVMCoreViewProtocol // MARK: - MVMCoreViewProtocol
extension Control: MVMCoreViewProtocol { extension Control: MVMCoreViewProtocol {
public func updateView(_ size: CGFloat) {} open func updateView(_ size: CGFloat) {}
/// Will be called only once. /// Will be called only once.
public func setupView() { open func setupView() {
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
insetsLayoutMarginsFromSafeArea = false insetsLayoutMarginsFromSafeArea = false
} }
@ -72,16 +89,15 @@ extension Control: MVMCoreViewProtocol {
// MARK: - MVMCoreUIMoleculeViewProtocol // MARK: - MVMCoreUIMoleculeViewProtocol
extension Control: 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]?) { open func reset() {
self.json = json
if let backgroundColorString = json?.optionalStringForKey(KeyBackgroundColor) {
backgroundColor = UIColor.mfGet(forHex: backgroundColorString)
}
}
public func reset() {
backgroundColor = .clear backgroundColor = .clear
} }
} }

View File

@ -8,12 +8,9 @@
import UIKit import UIKit
@objcMembers open class View: UIView { @objcMembers open class View: UIView, ModelMoleculeViewProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
open var json: [AnyHashable: Any]? open var json: [AnyHashable: Any]?
open var model: MoleculeModelProtocol?
private var initialSetupPerformed = false private var initialSetupPerformed = false
@ -36,12 +33,31 @@ import UIKit
} }
public func initialSetup() { public func initialSetup() {
if !initialSetupPerformed { if !initialSetupPerformed {
initialSetupPerformed = true initialSetupPerformed = true
setupView() 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 // MARK:- MVMCoreViewProtocol
@ -53,6 +69,7 @@ extension View: MVMCoreViewProtocol {
open func setupView() { open func setupView() {
translatesAutoresizingMaskIntoConstraints = false translatesAutoresizingMaskIntoConstraints = false
insetsLayoutMarginsFromSafeArea = false insetsLayoutMarginsFromSafeArea = false
MVMCoreUIUtility.setMarginsFor(self, leading: 0, top: 0, trailing: 0, bottom: 0)
} }
} }
@ -70,4 +87,8 @@ extension View: MVMCoreUIMoleculeViewProtocol {
open func reset() { open func reset() {
backgroundColor = .clear backgroundColor = .clear
} }
open func setAsMolecule() {
}
} }

View 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 {
}
}

View File

@ -27,14 +27,13 @@
#import <MVMCoreUI/ButtonDelegateProtocol.h> #import <MVMCoreUI/ButtonDelegateProtocol.h>
#import <MVMCoreUI/MFStyler.h> #import <MVMCoreUI/MFStyler.h>
#import <MVMCoreUI/MVMCoreUIDetailViewProtocol.h> #import <MVMCoreUI/MVMCoreUIDetailViewProtocol.h>
#import <MVMCoreUI/MoleculeDelegateProtocol.h>
@class MainMenuViewController; @class MainMenuViewController;
@class MVMCoreUITabBarPageControlViewController; @class MVMCoreUITabBarPageControlViewController;
@class MVMAnimationManager; @class MVMAnimationManager;
@class DelegateObject; @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. // Stores the load object that this screen was loaded with.
@property (nullable, strong, nonatomic) MVMCoreLoadObject *loadObject; @property (nullable, strong, nonatomic) MVMCoreLoadObject *loadObject;

View File

@ -97,6 +97,17 @@
self.pageType = loadObject.pageType; self.pageType = loadObject.pageType;
self.loadObject = loadObject; 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. // Verifies all modules needed are loaded.
return [MFViewController verifyRequiredModulesLoadedForLoadObject:loadObject error:error]; return [MFViewController verifyRequiredModulesLoadedForLoadObject:loadObject error:error];
} }
@ -249,6 +260,8 @@
- (BOOL)newPageLoaded:(nonnull NSDictionary *)page { - (BOOL)newPageLoaded:(nonnull NSDictionary *)page {
self.loadObject.pageJSON = page; self.loadObject.pageJSON = page;
NSError *parseError = nil;
[self parsePageJSONAndReturnError:&parseError];
return YES; 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 #pragma mark - adobe analytics
- (nullable NSArray <NSDictionary *> *)additionalActionsToTrackWithMainActionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData { - (nullable NSArray <NSDictionary *> *)additionalActionsToTrackWithMainActionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData {

View 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
}
}

View File

@ -29,7 +29,7 @@ import UIKit
let navigationController = self.init() let navigationController = self.init()
style(navigationController.navigationBar) style(navigationController.navigationBar)
navigationController.separatorView = Line(pinTo: navigationController.navigationBar, edge: .bottom, useMargin: false) navigationController.separatorView = Line(pinTo: navigationController.navigationBar, edge: .bottom, useMargin: false)
navigationController.separatorView?.style = .standard navigationController.separatorView?.setStyle(.standard)
MVMCoreUISession.sharedGlobal()?.navigationController = navigationController MVMCoreUISession.sharedGlobal()?.navigationController = navigationController
MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = navigationController MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = navigationController
MVMCoreNavigationHandler.shared()?.navigationController = navigationController MVMCoreNavigationHandler.shared()?.navigationController = navigationController

View 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?()
}
}

View File

@ -1,19 +1,13 @@
// //
// Container.swift // ContainerHelper.swift
// MVMCoreUI // MVMCoreUI
// //
// Created by Scott Pfeil on 12/11/19. // Created by Scott Pfeil on 1/21/20.
// Copyright © 2019 Verizon Wireless. All rights reserved. // Copyright © 2020 Verizon Wireless. All rights reserved.
// //
// A Helper that consolidates some common container logic
import UIKit import Foundation
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 }
}
public class ContainerHelper: NSObject { public class ContainerHelper: NSObject {
var leftConstraint: NSLayoutConstraint? 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? { static func getAlignment(for string: String) -> UIStackView.Alignment? {
switch string { switch string {
case "leading": 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) { 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?() { if let horizontalAlignmentString = JSON?.optionalStringForKey("horizontalAlignment"), let alignment = ContainerHelper.getAlignment(for: horizontalAlignmentString) ?? (contained as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() {
alignHorizontal(alignment) 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?()
}
}

View 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)
}
}

View 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)
}

View File

@ -118,7 +118,7 @@ import UIKit
//-------------------------------------------------- //--------------------------------------------------
/// Holds reference to delegateObject to inform molecular tableView of an update. /// Holds reference to delegateObject to inform molecular tableView of an update.
weak var delegateObject: MVMCoreUIDelegateObject? var delegateObject: MVMCoreUIDelegateObject?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Lifecycle // MARK: - Lifecycle
@ -266,14 +266,22 @@ import UIKit
let size: CGFloat = bottomBarSize ?? (showError ? 4 : 1) let size: CGFloat = bottomBarSize ?? (showError ? 4 : 1)
bottomBar?.frame = CGRect(x: 0, y: bounds.height - size, width: bounds.width, height: size) bottomBar?.frame = CGRect(x: 0, y: bounds.height - size, width: bounds.width, height: size)
delegateObject?.moleculeDelegate?.moleculeLayoutUpdated?(self) delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self)
setNeedsDisplay() setNeedsDisplay()
layoutIfNeeded() 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 { extension EntryFieldContainer {
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {

View 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)
}
}

View 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)
}
}

View 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)
}
}
}

View File

@ -14,6 +14,7 @@
@interface MFView : UIView <MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol> @interface MFView : UIView <MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol>
@property (nullable, nonatomic, strong) NSDictionary *json; @property (nullable, nonatomic, strong) NSDictionary *json;
@property (nullable, nonatomic, strong) id model;
// Called in the initialization functions. Can setup ui here. // Called in the initialization functions. Can setup ui here.
- (void)setupView; - (void)setupView;

View File

@ -10,14 +10,15 @@
@import MVMCore.MVMCoreViewProtocol; @import MVMCore.MVMCoreViewProtocol;
@class MVMCoreUIDelegateObject; @class MVMCoreUIDelegateObject;
@class MVMCoreErrorObject; @class MVMCoreErrorObject;
@class MoleculeModelProtocol;
@protocol MVMCoreUIMoleculeViewProtocol <NSObject, MVMCoreViewProtocol> @protocol MVMCoreUIMoleculeViewProtocol <NSObject, MVMCoreViewProtocol>
@optional
/// Sets up the ui based on the json /// Sets up the ui based on the json
- (void)setWithJSON:(nullable NSDictionary *)json delegateObject:(nullable MVMCoreUIDelegateObject *)delegateObject additionalData:(nullable NSDictionary *)additionalData; - (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 /// Called after init to provide an early setter for any molecule specific logic
- (void)setAsMolecule; - (void)setAsMolecule;

Some files were not shown because too many files have changed in this diff Show More