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
//------------------------------------------------------
@ -88,6 +89,8 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI
NSLayoutConstraint(item: caretView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: self, attribute: .right, relatedBy: .greaterThanOrEqual, toItem: caretView, attribute: .right, multiplier: 1.0, constant: 0).isActive = true
caretView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true
bottomAnchor.constraint(greaterThanOrEqualTo: caretView.bottomAnchor).isActive = true
contentHorizontalAlignment = .left
//set correct color after layout
@ -107,6 +110,7 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI
@objc open func setAsMolecule() {
backgroundColor = .clear
translatesAutoresizingMaskIntoConstraints = false
setTitleColor(enabledColor, for: .normal)
setTitleColor(disabledColor, for: .disabled)
}
@ -137,6 +141,20 @@ open class CaretButton: MFCustomButton, MVMCoreUIMoleculeViewProtocol, MVMCoreUI
}
}
public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard let caretLinkModel = model as? CaretLinkModel else { return }
if let color = caretLinkModel.backgroundColor {
backgroundColor = color.uiColor
}
enabledColor = caretLinkModel.enabledColor.uiColor
if let color = caretLinkModel.disabledColor {
disabledColor = color.uiColor
}
isEnabled = caretLinkModel.enabled
set(with: caretLinkModel.action, delegateObject: delegateObject, additionalData: additionalData)
setTitle(caretLinkModel.title, for: .normal)
}
public func needsToBeConstrained() -> Bool {
return true
}

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

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
}()
public var dateFormat: String?
public var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeZone = NSTimeZone.system
formatter.locale = .current
formatter.formatterBehavior = .default
return formatter
}()
public var dateFormat: String = "MMM d, y" {
didSet {
dateFormatter.dateFormat = dateFormat
}
}
//--------------------------------------------------
// MARK: - Initializers
@ -83,7 +96,7 @@ import UIKit
if calendar.isDate(date, inSameDayAs: Date()) {
text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string")
} else {
text = dateFormatter().string(from: date)
text = dateFormatter.string(from: date)
}
}
@ -98,19 +111,12 @@ import UIKit
setTextWith(date: datePicker?.date)
}
public func dateFormatter() -> DateFormatter {
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithModel(model, delegateObject, additionalData)
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeZone = NSTimeZone.system
formatter.locale = .current
formatter.formatterBehavior = .default
guard let model = model as? DateDropdownEntryFieldModel else { return }
if let dateFormat = dateFormat {
formatter.dateFormat = dateFormat
}
return formatter
dateFormat = model.dateFormat
}
}

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

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.accessibilityElementsHidden = feedbackLabel.text?.isEmpty ?? true
entryFieldContainer.refreshUI()
delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self)
}
}
@ -238,6 +239,31 @@ import UIKit
feedbackLabel.textColor = .black
entryFieldContainer.reset()
}
open override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithModel(model, delegateObject, additionalData)
self.delegateObject = delegateObject
guard let model = model as? EntryFieldModel else { return }
entryFieldContainer.setWithModel(model, delegateObject, additionalData)
title = model.title
feedback = model.feedback
errorMessage = model.errorMessage
isEnabled = model.isEnabled
if let isLocked = model.isLocked {
self.isLocked = isLocked
} else if let isSelected = model.isSelected{
self.isSelected = isSelected
}
if let fieldKey = model.fieldKey {
self.fieldKey = fieldKey
}
}
}
// MARK: - MVMCoreUIMoleculeViewProtocol

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
/// Closure passed here will run as picker changes items.
public var observeDropdownChange: ((String)->())?
public var observeDropdownChange: ((String, String)->())?
/// Closure passed here will run upon dismissing the selection picker.
public var observeDropdownSelection: ((String)->())?
@ -90,6 +90,19 @@ open class ItemDropdownEntryField: BaseDropdownEntryField {
observeDropdownSelection?(pickerData[pickerIndex])
}
}
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithModel(model, delegateObject, additionalData)
guard let model = model as? ItemDropdownEntryFieldModel else { return }
pickerData = model.options
setPickerDelegates(delegate: self)
if let pickerView = pickerView {
self.pickerView(pickerView, didSelectRow: 0, inComponent: 0)
}
}
}
// MARK:- Base Picker Delegate
@ -104,12 +117,16 @@ extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource {
}
@objc public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
guard !pickerData.isEmpty else { return nil }
return pickerData[row]
}
@objc public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
guard !pickerData.isEmpty else { return }
observeDropdownChange?(text ?? "", pickerData[row])
text = pickerData[row]
observeDropdownChange?(pickerData[row])
}
}

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

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.
@objc open func setAsMolecule() {
@objc open override func setAsMolecule() {
defaultState()
}
@objc open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
guard let dictionary = json else { return }
if let strokeColorHex = dictionary["strokeColor"] as? String {
strokeColor = UIColor.mfGet(forHex: strokeColorHex)
public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: CaretViewModel.self) else { return }
setWithModel(model, delegateObject, additionalData)
}
//MARK: - MVMCoreMoleculeViewProtocol
override public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithModel(model, delegateObject, additionalData)
guard let caretModel = model as? CaretViewModel else {
return
}
if let isHidden = dictionary[KeyIsHidden] as? Bool {
self.isHidden = isHidden
}
if let isOpaque = dictionary[KeyIsOpaque] as? Bool {
self.isOpaque = isOpaque
}
if let lineWidth = dictionary["lineWidth"] as? CGFloat {
self.lineWidth = lineWidth
strokeColor = caretModel.strokeColor.uiColor
isHidden = caretModel.isHidden ?? false
isOpaque = caretModel.isOpaque ?? false
if let lineWidthValue = caretModel.lineWidth {
lineWidth = lineWidthValue
}
}
}

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.
public var checkColor: UIColor = .black {
didSet {
@ -107,7 +128,6 @@ import MVMCore
if !updateSelectionOnly {
layoutIfNeeded()
shapeLayer?.removeAllAnimations()
updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated)
FormValidator.enableByValidationWith(delegate: delegateObject?.formValidationProtocol)
updateAccessibilityLabel()
@ -154,16 +174,12 @@ import MVMCore
public convenience init(isChecked: Bool) {
self.init(frame: .zero)
updateSelectionOnly = true
isSelected = isChecked
updateSelectionOnly = false
checkAndBypassAnimations(selected: isChecked)
}
public convenience init(checkedBackgroundColor: UIColor, unCheckedBackgroundColor: UIColor, isChecked: Bool = false) {
self.init(frame: .zero)
updateSelectionOnly = true
isSelected = isChecked
updateSelectionOnly = false
checkAndBypassAnimations(selected: isChecked)
self.checkedBackgroundColor = checkedBackgroundColor
self.unCheckedBackgroundColor = unCheckedBackgroundColor
}
@ -177,8 +193,6 @@ import MVMCore
drawShapeLayer()
layer.cornerRadius = isRound ? cornerRadiusValue : 0
layer.borderWidth = borderWidth
layer.borderColor = borderColor.cgColor
}
open override func setupView() {
@ -228,7 +242,7 @@ import MVMCore
self.shapeLayer = shapeLayer
shapeLayer.frame = bounds
layer.addSublayer(shapeLayer)
shapeLayer.strokeColor = checkColor.cgColor
shapeLayer.strokeColor = isEnabled ? checkColor.cgColor : disabledCheckColor.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.path = checkMarkPath()
shapeLayer.lineJoin = .miter
@ -268,9 +282,7 @@ import MVMCore
DispatchQueue.main.async {
self.updateSelectionOnly = true
self.isSelected = selected
self.updateSelectionOnly = false
self.checkAndBypassAnimations(selected: selected)
self.drawShapeLayer()
self.shapeLayer?.removeAllAnimations()
self.updateCheckboxUI(isSelected: selected, isAnimated: animated)
@ -313,23 +325,6 @@ import MVMCore
}
}
func isEnabled(_ enabled: Bool) {
isUserInteractionEnabled = enabled
if enabled {
layer.borderColor = borderColor.cgColor
backgroundColor = isSelected ? checkedBackgroundColor : unCheckedBackgroundColor
alpha = 1.0
setShapeLayerStrokeColor(checkColor)
} else {
layer.borderColor = UIColor.mfSilver().cgColor
backgroundColor = .clear
alpha = DisableOppacity
setShapeLayerStrokeColor(UIColor.mfSilver())
}
}
private func setShapeLayerStrokeColor(_ color: UIColor) {
if let shapeLayer = shapeLayer {
@ -345,13 +340,19 @@ import MVMCore
widthConstraint?.isActive = isActive
}
private func checkAndBypassAnimations(selected: Bool) {
updateSelectionOnly = true
isSelected = selected
updateSelectionOnly = false
}
//--------------------------------------------------
// MARK: - UITouch
//--------------------------------------------------
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
sendActions(for: .touchUpInside)
sendActions(for: .touchUpInside)
}
override open func accessibilityActivate() -> Bool {
@ -370,7 +371,7 @@ import MVMCore
open override func reset() {
super.reset()
isEnabled(true)
isEnabled = true
shapeLayer?.removeAllAnimations()
shapeLayer?.removeFromSuperlayer()
shapeLayer = nil
@ -379,9 +380,7 @@ import MVMCore
borderWidth = 1.0
checkColor = .black
checkWidth = 2.0
updateSelectionOnly = true
isSelected = false
updateSelectionOnly = false
checkAndBypassAnimations(selected: false)
}
open func setAsMolecule() {
@ -424,16 +423,14 @@ import MVMCore
layer.borderWidth = borderWidth
}
if let isChecked = dictionary["isChecked"] as? Bool, isChecked {
updateSelectionOnly = true
isSelected = isChecked
updateSelectionOnly = false
}
if let checkColorHex = dictionary["checkColor"] as? String {
checkColor = UIColor.mfGet(forHex: checkColorHex)
}
if let isChecked = dictionary["isChecked"] as? Bool, isChecked {
checkAndBypassAnimations(selected: isChecked)
}
if let unCheckedBackgroundColorHex = dictionary["unCheckedBackgroundColor"] as? String {
unCheckedBackgroundColor = UIColor.mfGet(forHex: unCheckedBackgroundColorHex)
}
@ -451,13 +448,57 @@ import MVMCore
}
if let enabled = dictionary["isEnabled"] as? Bool {
isEnabled(enabled)
isEnabled = enabled
}
if let actionMap = dictionary.optionalDictionaryForKey("actionMap") {
if let actionMap = dictionary.optionalDictionaryForKey("action") {
actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) }
}
}
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithModel(model, delegateObject, additionalData)
guard let model = model as? CheckboxModel else { return }
self.delegateObject = delegateObject
FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol)
groupName = model.groupName
fieldValue = model.value
isRequired = model.required
if let fieldKey = model.fieldKey {
self.fieldKey = fieldKey
}
borderColor = model.borderColor.uiColor
borderWidth = model.borderWidth
checkColor = model.checkColor.uiColor
unCheckedBackgroundColor = model.unCheckedBackgroundColor.uiColor
checkedBackgroundColor = model.checkedBackgroundColor.uiColor
disabledCheckColor = model.disabledCheckColor.uiColor
disabledBorderColor = model.disabledBorderColor.uiColor
disabledBackgroundColor = model.disabledBackgroundColor.uiColor
isAnimated = model.isAnimated
isRound = model.isRound
if model.isChecked {
checkAndBypassAnimations(selected: model.isChecked)
}
isEnabled = model.isEnabled
if let action = model.action {
actionBlock = {
if let actionMap = action.toJSON() {
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
}
}
}
}
}
// MARK:- FormValidationProtocol

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 enum CheckboxPosition: String {
case center
case top
case bottom
}
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
@ -120,6 +114,17 @@
checkboxCenterYConstraint?.isActive = false
}
}
open override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard let checkBoxWithLabelModel = model as? CheckboxLabelModel else { return }
if let checkboxAlignment = checkBoxWithLabelModel.checkboxAlignment {
alignCheckbox(checkboxAlignment)
}
checkbox.setWithModel(checkBoxWithLabelModel.checkbox, delegateObject, additionalData)
label.setWithModel(checkBoxWithLabelModel.label, delegateObject, additionalData)
}
}
// MARK: - Molecular

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

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
enum GraphSize: String {
case small, medium, large
}
enum GraphStyle: String {
case unlimited, safetyMode
}
///Graph Object contains properties
public struct GraphObject {
var style: GraphStyle {
didSet {
updateStyle()
}
}
var size: GraphSize {
didSet {
updateSize()
}
}
var diameter: CGFloat = 24
var lineWidth: CGFloat = 5
var clockwise: Bool = true
var duration : Double = 1.0
var colors = [CGColor]()
public init(_ json: [AnyHashable : Any]?) {
style = .unlimited
size = .small
guard let json = json else {
return
}
if let styleString = json.optionalStringForKey("style") {
style = GraphStyle(rawValue: styleString) ?? .unlimited
}
if let sizeString = json.optionalStringForKey("size") {
size = GraphSize(rawValue: sizeString) ?? .small
}
updateStyle()
updateSize()
if let diameter = json.optionalCGFloatForKey("diameter") {
self.diameter = diameter
}
if let lineWidth = json.optionalCGFloatForKey("lineWidth") {
self.lineWidth = lineWidth
}
if let clockwise = json.optionalBoolForKey("clockwise") {
self.clockwise = clockwise
}
if let duration = json["duration"] as? Double {
self.duration = duration
}
if let colorArray = json.optionalArrayForKey("colors") as? [String] {
colors = getCGColorsFromArray(colorArray)
}
}
func getCGColorsFromArray(_ colorArray: [String]) -> [CGColor] {
return colorArray.map { (colorString) -> CGColor in
return UIColor.mfGet(forHex: colorString).cgColor
}
}
mutating func updateStyle() {
switch style {
case .unlimited:
duration = 1.0
clockwise = true
//current style, only the end part shows darker look
colors = getCGColorsFromArray(["#007AB8","#007AB8","#033554"])
break
case .safetyMode:
duration = 1.5
clockwise = true
colors = getCGColorsFromArray(["#CC4D0F","#CC4D0F","AB0309"])
break
}
}
//those are
mutating func updateSize() {
switch size {
case .small:
diameter = MFSizeObject(standardSize: 20)?.getValueBasedOnApplicationWidth() ?? 20
lineWidth = MFSizeObject(standardSize: 4)?.getValueBasedOnApplicationWidth() ?? 4
break
case .medium:
diameter = MFSizeObject(standardSize: 100)?.getValueBasedOnApplicationWidth() ?? 100
lineWidth = MFSizeObject(standardSize: 8)?.getValueBasedOnApplicationWidth() ?? 8
break
case .large:
diameter = MFSizeObject(standardSize: 180)?.getValueBasedOnApplicationWidth() ?? 180
lineWidth = MFSizeObject(standardSize: 12)?.getValueBasedOnApplicationWidth() ?? 12
break
}
}
}
@objcMembers open class GraphView: View, MVMCoreUIViewConstrainingProtocol {
var heightConstraint: NSLayoutConstraint?
var gradientLayer: CALayer?
var graphObject: GraphObject?
var graphModel: CircleProgressModel? {
return model as? CircleProgressModel
}
// MARK: setup
open override func setupView() {
@ -125,13 +25,17 @@ public struct GraphObject {
heightConstraint?.isActive = true
widthAnchor.constraint(equalTo: heightAnchor).isActive = true
}
override open func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
let object = GraphObject(json)
graphObject = object
createGraphCircle(object)
rotationAnimation(object)
override open func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithModel(model, delegateObject, additionalData)
guard let model = model as? CircleProgressModel else { return }
createGraphCircle(model)
rotationAnimation(model)
}
override open func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: CircleProgressModel.self) else { return }
setWithModel(model, delegateObject, additionalData)
}
class func getAngle(_ piValue: Double) -> Double {
@ -143,7 +47,7 @@ public struct GraphObject {
}
// MARK: circle
open func createGraphCircle(_ graphObject: GraphObject) {
open func createGraphCircle(_ graphObject: CircleProgressModel) {
if let sublayers = layer.sublayers {
for sublayer in sublayers {
sublayer.removeAllAnimations()
@ -184,14 +88,14 @@ public struct GraphObject {
| | |
-------------
*/
func createGradientLayer(_ graphObject: GraphObject) -> CALayer {
func createGradientLayer(_ graphObject: CircleProgressModel) -> CALayer {
let containLayer = CALayer()
containLayer.frame = CGRect(x: 0, y: 0, width: graphObject.diameter, height: graphObject.diameter)
let radius = graphObject.diameter / 2.0
//create graident layers
guard graphObject.colors.count > 1 else {
containLayer.backgroundColor = graphObject.colors.first
containLayer.backgroundColor = graphObject.colors.first?.uiColor.cgColor
return containLayer
}
var topGradientHeight : CGFloat = 0.0
@ -208,9 +112,11 @@ public struct GraphObject {
//if number of colors is even, need to display gradient layer, otherwise make top layer as solid color layer
if graphObject.colors.count % 2 == 0 {
leftColors.removeLast()
topLayer.colors = [leftColors.last!, rightColors.first!]
let firstColor = leftColors.last!.uiColor.cgColor
let secondColor = rightColors.first!.uiColor.cgColor
topLayer.colors = [firstColor, secondColor]
} else {
topLayer.backgroundColor = leftColors.last
topLayer.backgroundColor = leftColors.last?.uiColor.cgColor
}
containLayer.addSublayer(topLayer)
@ -221,9 +127,11 @@ public struct GraphObject {
//count of graidentLayer.colors must be bigger than 1, otherwise set backgroundColor
if leftColors.count > 1 {
leftLayer.colors = Array(leftColors)
leftLayer.colors = leftColors.map({ (color) -> CGColor in
return color.uiColor.cgColor
})
} else {
leftLayer.backgroundColor = leftColors.first
leftLayer.backgroundColor = leftColors.first?.uiColor.cgColor
}
containLayer.addSublayer(leftLayer)
@ -232,9 +140,11 @@ public struct GraphObject {
rightLayer.startPoint = CGPoint(x: 0, y: 0)
rightLayer.endPoint = CGPoint(x: 0, y: 1)
if rightColors.count > 1 {
rightLayer.colors = Array(rightColors)
rightLayer.colors = rightColors.map({ (color) -> CGColor in
return color.uiColor.cgColor
})
} else {
rightLayer.backgroundColor = rightColors.first
rightLayer.backgroundColor = rightColors.first?.uiColor.cgColor
}
containLayer.addSublayer(rightLayer)
@ -246,7 +156,7 @@ public struct GraphObject {
}
//MARK: Animation
func rotationAnimation(_ object: GraphObject) {
func rotationAnimation(_ object: CircleProgressModel) {
MVMCoreDispatchUtility.performBlock(onMainThread:{
let rotation = CABasicAnimation(keyPath: "transform.rotation")
let animationHandler = GraphViewAnimationHandler.shared
@ -277,7 +187,7 @@ public struct GraphObject {
extension GraphView: CAAnimationDelegate {
public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if let object = graphObject {
if let object = graphModel {
rotationAnimation(object)
}
}

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 = () -> ()
@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol {
@objcMembers open class Label: UILabel, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol, MFButtonProtocol, ModelMoleculeViewProtocol {
//------------------------------------------------------
// MARK: - Properties
//------------------------------------------------------
@ -212,6 +213,123 @@ public typealias ActionBlock = () -> ()
}
}
enum LabelAlignment: String {
case center
case right
case left
}
public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
clauses = []
guard let labelModel = model as? LabelModel else { return }
attributedText = nil
text = labelModel.text
Label.setLabel(self, withHTML: labelModel.html)
let alignment = LabelAlignment(rawValue: labelModel.textAlignment ?? "")
switch alignment {
case .center:
textAlignment = .center
case .right:
textAlignment = .right
default:
textAlignment = .left
}
makeWholeViewClickable = labelModel.makeWholeViewClickable ?? false
if let backgroundColor = labelModel.backgroundColor {
self.backgroundColor = backgroundColor.uiColor
}
if let accessibilityText = labelModel.accessibilityText {
accessibilityLabel = accessibilityText
}
if let fontStyle = labelModel.fontStyle {
MFStyler.styleLabel(self, withStyle: fontStyle)
MFStyler.styleLabel(self, withStyle: fontStyle, genericScaling: false)
standardFontSize = font.pointSize
} else {
let fontSize = labelModel.fontSize
if let fontSize = fontSize {
standardFontSize = fontSize
}
if let fontName = labelModel.fontName {
font = MFFonts.mfFont(withName: fontName, size: fontSize ?? standardFontSize)
} else if let fontSize = fontSize {
font = font.withSize(fontSize)
}
}
if let textColorHex = labelModel.textColor, !textColorHex.isEmpty {
textColor = UIColor.mfGet(forHex: textColorHex)
}
if let attributes = labelModel.attributes, let labelText = text {
let attributedString = NSMutableAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font.withSize(standardFontSize), NSAttributedString.Key.foregroundColor: textColor as UIColor])
for attribute in attributes {
let range = NSRange(location: attribute.location, length: attribute.length)
switch attribute {
case _ as LabelAttributeUnderlineModel:
attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range)
case _ as LabelAttributeStrikeThroughModel:
attributedString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: range)
attributedString.addAttribute(.baselineOffset, value: 0, range: range)
case let colorAtt as LabelAttributeColorModel:
if let colorHex = colorAtt.textColor, !colorHex.isEmpty {
attributedString.removeAttribute(.foregroundColor, range: range)
attributedString.addAttribute(.foregroundColor, value: UIColor.mfGet(forHex: colorHex), range: range)
}
case let imageAtt as LabelAttributeImageModel:
var fontSize = font.pointSize
if let attributeSize = imageAtt.size {
fontSize = attributeSize
}
let imageName = imageAtt.name ?? "externalLink"
let imageAttachment: NSTextAttachment
if let url = imageAtt.URL {
imageAttachment = Label.getTextAttachmentFrom(url: url, dimension: fontSize, label: self)
} else {
imageAttachment = Label.getTextAttachmentImage(name: imageName, dimension: fontSize)
}
let mutableString = NSMutableAttributedString()
mutableString.append(NSAttributedString(attachment: imageAttachment))
attributedString.insert(mutableString, at: imageAtt.location)
case let fontAtt as LabelAttributeFontModel:
if let fontStyle = fontAtt.style {
let styles = MFStyler.styleGetAttributedString("0", withStyle: fontStyle)
attributedString.removeAttribute(.font, range: range)
attributedString.removeAttribute(.foregroundColor, range: range)
attributedString.addAttributes(styles.attributes(at: 0, effectiveRange: nil), range: range)
} else {
let fontSize = fontAtt.size
var font: UIFont?
if let fontName = fontAtt.name {
font = MFFonts.mfFont(withName: fontName, size: fontSize ?? self.font.pointSize)
} else if let fontSize = fontSize {
font = self.font.withSize(fontSize)
}
if let font = font {
attributedString.removeAttribute(.font, range: range)
attributedString.addAttribute(.font, value: font, range: range)
}
}
case let actionAtt as LabelAttributeActionModel:
addTappableLinkAttribute(range: NSRange(location: range.location, length: range.length)) {
if let data = try? actionAtt.action.encode(using: JSONEncoder()), let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any] {
MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject)
}
}
addActionAttributes(range: range, string: attributedString)
default:
continue
}
}
attributedText = attributedString
originalAttributedString = attributedText
hero = labelModel.hero
}
}
@objc public static func setUILabel(_ label: UILabel?, withJSON json: [AnyHashable: Any]?, delegate: DelegateObject?, additionalData: [AnyHashable: Any]?) {
guard let label = label else { return }

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
@objcMembers open class LeftRightLabelView: ViewConstrainingView {
@objcMembers open class LeftRightLabelView: View {
//------------------------------------------------------
// MARK: - Outlets
//------------------------------------------------------
@ -28,8 +28,8 @@ import Foundation
// MARK: - Initialization
//------------------------------------------------------
public init() {
super.init(frame: .zero)
public convenience init() {
self.init(frame: .zero)
}
public override init(frame: CGRect) {
@ -176,4 +176,20 @@ import Foundation
constrainLeftLabel()
}
}
//MARK: - MVMCoreMoleculeViewProtocol
open override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithModel(model, delegateObject, additionalData)
guard let leftRightLabelModel = model as? LeftRightLabelModel else {
return
}
leftTextLabel.setWithModel(leftRightLabelModel.leftText, delegateObject, additionalData)
rightTextLabel.setWithModel(leftRightLabelModel.rightText, delegateObject, additionalData)
if !leftTextLabel.hasText {
constrainRightLabel()
} else if !rightTextLabel.hasText {
constrainLeftLabel()
}
}
}

View File

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

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
@objcMembers open class MFLoadImageView: ViewConstrainingView {
@objcMembers open class MFLoadImageView: ViewConstrainingView, ModelMoleculeViewProtocol {
public let loadingSpinner = MFLoadingSpinner(frame: .zero)
public let imageView = MFTransparentGIFView(frame: .zero)
public var addSizeConstraintsForAspectRatio = false
@ -209,6 +209,31 @@ import UIKit
}
}
public func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard let imageModel = model as? ImageViewModel else {
return
}
if let accessibilityString = imageModel.accessibilityText {
imageView.accessibilityLabel = accessibilityString
imageView.accessibilityTraits = .staticText
imageView.isAccessibilityElement = true
}
let width = imageModel.width ?? imageWidth
let height = imageModel.height ?? imageHeight
// For smoother transitions, set constraints that we know immediately.
if let width = width, addSizeConstraintsForAspectRatio {
setWidth(width)
}
if let height = height, addSizeConstraintsForAspectRatio {
setHeight(height)
}
if shouldLoadImage(withName: imageModel.image, width: width, height: height) {
imageView.image = nil
imageView.animatedImage = nil
loadImage(withName: imageModel.image, format: imageModel.imageFormat, width: width as NSNumber?, height: height as NSNumber?, customFallbackImage: imageModel.fallbackImage)
}
}
// MARK: - MVMCoreUIMoleculeViewProtocol functions
open override func setAsMolecule() {
addSizeConstraintsForAspectRatio = true
@ -264,7 +289,7 @@ import UIKit
self?.addConstraints(width: width, height: height, size: image?.size)
self?.loadingSpinnerHeightConstraint?.constant = 0
if layoutWillChange {
self?.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated?(self!)
self?.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self!)
}
completionHandler(image,data,isFallbackImage)
})}

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
@objcMembers open class ProgressBarObject {
///from 0.0 to 1.0. input progress should be [0 100]
var progress: CGFloat = 0.0
///default color is cerulean
var color: UIColor = UIColor.mfCerulean()
init(_ module: [AnyHashable: Any]?) {
progress = (module?.optionalCGFloatForKey("progress") ?? 0.0)/100
if let colorString = module?.optionalStringForKey("progressColor") {
color = UIColor.mfGet(forHex: colorString)
}
}
static func getProgressBarObjectList(_ list: [[AnyHashable: Any]]?) -> [ProgressBarObject]? {
guard list?.count ?? 0 > 0 else {
return nil
}
var progressList = [ProgressBarObject]()
for module in list! {
let progressObject = ProgressBarObject(module)
progressList.append(progressObject)
}
return progressList
}
}
@objcMembers open class MultiProgress: View {
var multiProgressModel: MultiProgressBarModel? {
get { return model as? MultiProgressBarModel }
}
///passing value to progressList creates corresponding progress bars
var progressList: Array<ProgressBarObject>? {
var progressList: Array<SingleProgressBarModel>? {
didSet {
for subview in subviews {
subview.removeFromSuperview()
@ -52,7 +30,7 @@ import UIKit
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
addSubview(view)
view.backgroundColor = progressObject.color
view.backgroundColor = progressObject.progressColor.uiColor
view.widthAnchor.constraint(equalTo: widthAnchor, multiplier: progressObject.progress).isActive = true
view.leadingAnchor.constraint(equalTo: previous?.trailingAnchor ?? leadingAnchor).isActive = true
previous = view
@ -70,6 +48,7 @@ import UIKit
}
}
}
var thicknessConstraint: NSLayoutConstraint?
let defaultHeight: CGFloat = 8
@ -84,16 +63,26 @@ import UIKit
}
}
//MARK: - MVMCoreMoleculeViewProtocol
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithModel(model, delegateObject, additionalData)
guard let multiProgressModel = multiProgressModel else {
return
}
roundedRect = multiProgressModel.roundedRect ?? false
thicknessConstraint?.constant = multiProgressModel.thickness ?? defaultHeight
progressList = multiProgressModel.progressList
}
open override func reset() {
super.reset()
backgroundColor = .mfLightSilver()
progressList = nil
}
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
thicknessConstraint?.constant = json?.optionalCGFloatForKey("thickness") ?? defaultHeight
roundedRect = json?.optionalBoolForKey("roundedRect") ?? false
progressList = ProgressBarObject.getProgressBarObjectList(json?.optionalArrayForKey("progressList") as? [[AnyHashable: Any]])
public override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
guard let json = json, let model = try? Self.decodeJSONToModel(json: json, type: MultiProgressBarModel.self) else { return }
setWithModel(model, delegateObject, additionalData)
}
}

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

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()
}
}
// MARK:- ModelMoleculeViewProtocol
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
guard let toggleModel = model as? ToggleModel else {
return
}
let toggleModelJSON = toggleModel.toJSON()
setWithJSON(toggleModelJSON, delegateObject: delegateObject, additionalData: additionalData)
}
public override class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return Self.getContainerHeight()
}
}
// MARK: - Accessibility
@ -389,7 +403,7 @@ extension Toggle {
changeStateNoAnimation(state)
}
if let actionMap = dictionary.optionalDictionaryForKey("actionMap") {
if let actionMap = dictionary.optionalDictionaryForKey("action") {
didToggleAction = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) }
}
@ -410,7 +424,7 @@ extension Toggle {
return true
}
public func alignment() -> UIStackView.Alignment {
public func horizontalAlignment() -> UIStackView.Alignment {
return .trailing
}
}

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

View File

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

View File

@ -8,12 +8,9 @@
import UIKit
@objcMembers open class View: UIView {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@objcMembers open class View: UIView, ModelMoleculeViewProtocol {
open var json: [AnyHashable: Any]?
open var model: MoleculeModelProtocol?
private var initialSetupPerformed = false
@ -36,12 +33,31 @@ import UIKit
}
public func initialSetup() {
if !initialSetupPerformed {
initialSetupPerformed = true
setupView()
}
}
// MARK:- ModelMoleculeViewProtocol
open func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
self.model = model
if let backgroundColor = model?.backgroundColor {
self.backgroundColor = backgroundColor.uiColor
}
}
open class func nameForReuse(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?) -> String? {
return model?.moleculeName
}
open class func estimatedHeight(forRow molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? {
return nil
}
open class func requiredModules(_ molecule: MoleculeModelProtocol?, delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer<MVMCoreErrorObject?>?) -> [String]? {
return nil
}
}
// MARK:- MVMCoreViewProtocol
@ -53,6 +69,7 @@ extension View: MVMCoreViewProtocol {
open func setupView() {
translatesAutoresizingMaskIntoConstraints = false
insetsLayoutMarginsFromSafeArea = false
MVMCoreUIUtility.setMarginsFor(self, leading: 0, top: 0, trailing: 0, bottom: 0)
}
}
@ -70,4 +87,8 @@ extension View: MVMCoreUIMoleculeViewProtocol {
open func reset() {
backgroundColor = .clear
}
open func setAsMolecule() {
}
}

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/MFStyler.h>
#import <MVMCoreUI/MVMCoreUIDetailViewProtocol.h>
#import <MVMCoreUI/MoleculeDelegateProtocol.h>
@class MainMenuViewController;
@class MVMCoreUITabBarPageControlViewController;
@class MVMAnimationManager;
@class DelegateObject;
@interface MFViewController : UIViewController <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol, MVMCoreActionDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, MFTextFieldDelegate, ButtonDelegateProtocol, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MVMCoreUIDetailViewProtocol, MoleculeDelegateProtocol>
@interface MFViewController : UIViewController <MVMCoreLoadDelegateProtocol, MVMCorePresentationDelegateProtocol, MVMCoreActionDelegateProtocol, UITextFieldDelegate, UITextViewDelegate, MFTextFieldDelegate, ButtonDelegateProtocol, MVMCoreViewControllerProtocol, MVMCoreViewManagerViewControllerProtocol, MVMCoreUIDetailViewProtocol>
// Stores the load object that this screen was loaded with.
@property (nullable, strong, nonatomic) MVMCoreLoadObject *loadObject;

View File

@ -97,6 +97,17 @@
self.pageType = loadObject.pageType;
self.loadObject = loadObject;
NSError *parseError = nil;
[self parsePageJSONAndReturnError:&parseError];
if (parseError) {
if (error) {
MVMCoreErrorObject *errorObject = [MVMCoreErrorObject createErrorObjectForNSError:parseError location:[[MVMCoreLoadHandler sharedGlobal] errorLocationForRequest:loadObject]];
errorObject.messageToDisplay = [MVMCoreGetterUtility hardcodedStringWithKey:HardcodedErrorUnableToProcess];
*error = errorObject;
}
return false;
}
// Verifies all modules needed are loaded.
return [MFViewController verifyRequiredModulesLoadedForLoadObject:loadObject error:error];
}
@ -249,6 +260,8 @@
- (BOOL)newPageLoaded:(nonnull NSDictionary *)page {
self.loadObject.pageJSON = page;
NSError *parseError = nil;
[self parsePageJSONAndReturnError:&parseError];
return YES;
}
@ -832,15 +845,6 @@
}
}
#pragma mark - MoleculeDelegateProtocol
- (NSDictionary *)getModuleWithName:(NSString *)name {
if (!name) {
return nil;
}
return [self.loadObject.modulesJSON dict:name];
}
#pragma mark - adobe analytics
- (nullable NSArray <NSDictionary *> *)additionalActionsToTrackWithMainActionInformation:(nullable NSDictionary *)actionInformation additionalData:(nullable NSDictionary *)additionalData {

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()
style(navigationController.navigationBar)
navigationController.separatorView = Line(pinTo: navigationController.navigationBar, edge: .bottom, useMargin: false)
navigationController.separatorView?.style = .standard
navigationController.separatorView?.setStyle(.standard)
MVMCoreUISession.sharedGlobal()?.navigationController = navigationController
MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = navigationController
MVMCoreNavigationHandler.shared()?.navigationController = navigationController

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
//
// Created by Scott Pfeil on 12/11/19.
// Copyright © 2019 Verizon Wireless. All rights reserved.
// Created by Scott Pfeil on 1/21/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
// A Helper that consolidates some common container logic
import UIKit
public protocol ContainerModelProtocol {
var horizontalAlignment: UIStackView.Alignment? { get set }
var verticalAlignment: UIStackView.Alignment? { get set }
var useHorizontalMargins: Bool? { get set }
var useVerticalMargins: Bool? { get set }
}
import Foundation
public class ContainerHelper: NSObject {
var leftConstraint: NSLayoutConstraint?
@ -145,15 +139,6 @@ public class ContainerHelper: NSObject {
}
}
func set(with model: ContainerModelProtocol) {
if let horizontalAlignment = model.horizontalAlignment {
alignHorizontal(horizontalAlignment)
}
if let verticalAlignment = model.verticalAlignment {
alignVertical(verticalAlignment)
}
}
static func getAlignment(for string: String) -> UIStackView.Alignment? {
switch string {
case "leading":
@ -169,6 +154,34 @@ public class ContainerHelper: NSObject {
}
}
static func getAlignmentString(for alignment: UIStackView.Alignment?) -> String? {
switch alignment {
case .leading:
return "leading"
case .trailing:
return "trailing"
case .center:
return "center"
case .fill:
return "fill"
default:
return nil
}
}
func updateViewMargins(_ view: UIView, model: ContainerModelProtocol?, size: CGFloat) {
MFStyler.setMarginsFor(view, size: size, defaultHorizontal: model?.useHorizontalMargins ?? false, top: (model?.useVerticalMargins ?? false) ? (model?.topMarginPadding ?? 0) : 0, bottom: (model?.useVerticalMargins ?? false) ? (model?.bottomMarginPadding ?? 0) : 0)
}
func set(with model: ContainerModelProtocol, for contained: MVMCoreUIViewConstrainingProtocol?) {
if let horizontalAlignment = model.horizontalAlignment ?? contained?.horizontalAlignment?() {
alignHorizontal(horizontalAlignment)
}
if let verticalAlignment = model.verticalAlignment ?? contained?.verticalAlignment?() {
alignVertical(verticalAlignment)
}
}
func set(with JSON: [AnyHashable: Any]?, for contained: UIView) {
if let horizontalAlignmentString = JSON?.optionalStringForKey("horizontalAlignment"), let alignment = ContainerHelper.getAlignment(for: horizontalAlignmentString) ?? (contained as? MVMCoreUIViewConstrainingProtocol)?.horizontalAlignment?() {
alignHorizontal(alignment)
@ -183,57 +196,3 @@ public class ContainerHelper: NSObject {
}
}
}
open class Container: View {
var model: ContainerModelProtocol?
var view: UIView?
let containerHelper = ContainerHelper()
var topMarginPadding: CGFloat = 0
var bottomMarginPadding: CGFloat = 0
}
// MARK: - MVMCoreViewProtocol
public extension Container {
override func updateView(_ size: CGFloat) {
super.updateView(size)
(view as? MVMCoreViewProtocol)?.updateView(size)
MFStyler.setMarginsFor(self, size: size, defaultHorizontal: model?.useHorizontalMargins ?? true, top: model?.useHorizontalMargins ?? true ? topMarginPadding : 0, bottom: model?.useHorizontalMargins ?? true ? bottomMarginPadding : 0)
}
/// Will be called only once.
override func setupView() {
super.setupView()
backgroundColor = .clear
}
func addAndContain(_ view: UIView) {
view.translatesAutoresizingMaskIntoConstraints = false
addSubview(view)
containerHelper.constrainView(view)
self.view = view
}
convenience init(andContain view: UIView) {
self.init()
addAndContain(view)
}
}
// MARK: - MVMCoreUIMoleculeViewProtocol
public extension Container {
override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
guard let view = view else { return }
containerHelper.set(with: json, for: view)
}
override func reset() {
super.reset()
(view as? MVMCoreUIMoleculeViewProtocol)?.reset?()
}
func setAsMolecule() {
(view as? MVMCoreUIMoleculeViewProtocol)?.setAsMolecule?()
}
}

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

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>
@property (nullable, nonatomic, strong) NSDictionary *json;
@property (nullable, nonatomic, strong) id model;
// Called in the initialization functions. Can setup ui here.
- (void)setupView;

View File

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

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