work in progress

This commit is contained in:
Kevin G Christiano 2020-02-13 12:42:38 -05:00
parent 4577274e08
commit fd72f5ff95
12 changed files with 323 additions and 369 deletions

View File

@ -63,7 +63,6 @@
0A14F69323E349EF00EDF7F7 /* CarouselIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A14F69223E349EF00EDF7F7 /* CarouselIndicator.swift */; };
0A14F6A523E4803A00EDF7F7 /* StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A14F6A423E4803A00EDF7F7 /* StackView.swift */; };
0A14F6A923E8750300EDF7F7 /* CarouselIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A14F6A823E8750300EDF7F7 /* CarouselIndicatorModel.swift */; };
0A14F6B223E8C28D00EDF7F7 /* BarsIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A14F6B123E8C28D00EDF7F7 /* BarsIndicatorView.swift */; };
0A14F6B423E8C29700EDF7F7 /* NumericIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A14F6B323E8C29700EDF7F7 /* NumericIndicatorView.swift */; };
0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxLabel.swift */; };
0A21DB7F235DECC500C160A2 /* EntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A21DB7E235DECC500C160A2 /* EntryField.swift */; };
@ -94,7 +93,10 @@
0A7EF86323D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86223D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift */; };
0A7EF86523D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */; };
0A7EF86723D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86623D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift */; };
0A9F3DE823EDE9F200318918 /* Arrow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9F3DE723EDE9F200318918 /* Arrow.swift */; };
0A9F3DEA23EDEA1A00318918 /* ArrowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9F3DE923EDEA1A00318918 /* ArrowModel.swift */; };
0AA33B3A2398524F0067DD0F /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AA33B392398524F0067DD0F /* Toggle.swift */; };
0AB2AA2323F19CFA00C6D3CF /* BarsIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB2AA2223F19CFA00C6D3CF /* BarsIndicatorView.swift */; };
0ABD136D237CAD1E0081388D /* DateDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */; };
0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */; };
0AE14F64238315D2005417F8 /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE14F63238315D2005417F8 /* TextField.swift */; };
@ -394,7 +396,6 @@
0A14F69223E349EF00EDF7F7 /* CarouselIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselIndicator.swift; sourceTree = "<group>"; };
0A14F6A423E4803A00EDF7F7 /* StackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackView.swift; sourceTree = "<group>"; };
0A14F6A823E8750300EDF7F7 /* CarouselIndicatorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselIndicatorModel.swift; sourceTree = "<group>"; };
0A14F6B123E8C28D00EDF7F7 /* BarsIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarsIndicatorView.swift; sourceTree = "<group>"; };
0A14F6B323E8C29700EDF7F7 /* NumericIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumericIndicatorView.swift; sourceTree = "<group>"; };
0A209CD223A7E2810068F8B0 /* UIStackViewAlignment+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIStackViewAlignment+Extension.swift"; sourceTree = "<group>"; };
0A21DB7E235DECC500C160A2 /* EntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryField.swift; sourceTree = "<group>"; };
@ -415,8 +416,11 @@
0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDropdownEntryFieldModel.swift; sourceTree = "<group>"; };
0A7EF86623D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateDropdownEntryFieldModel.swift; sourceTree = "<group>"; };
0A8321AE2355FE9500CB7F00 /* DigitBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigitBox.swift; sourceTree = "<group>"; };
0A9F3DE723EDE9F200318918 /* Arrow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arrow.swift; sourceTree = "<group>"; };
0A9F3DE923EDEA1A00318918 /* ArrowModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowModel.swift; sourceTree = "<group>"; };
0AA33B33239813C50067DD0F /* UIColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extension.swift"; sourceTree = "<group>"; };
0AA33B392398524F0067DD0F /* Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toggle.swift; sourceTree = "<group>"; };
0AB2AA2223F19CFA00C6D3CF /* BarsIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarsIndicatorView.swift; sourceTree = "<group>"; };
0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateDropdownEntryField.swift; sourceTree = "<group>"; };
0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDropdownEntryField.swift; sourceTree = "<group>"; };
0AE14F63238315D2005417F8 /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = "<group>"; };
@ -742,7 +746,7 @@
0A14F6B723ECA7F900EDF7F7 /* IndicatorViews */ = {
isa = PBXGroup;
children = (
0A14F6B123E8C28D00EDF7F7 /* BarsIndicatorView.swift */,
0AB2AA2223F19CFA00C6D3CF /* BarsIndicatorView.swift */,
0A14F6B323E8C29700EDF7F7 /* NumericIndicatorView.swift */,
);
path = IndicatorViews;
@ -1241,6 +1245,8 @@
0AA33B392398524F0067DD0F /* Toggle.swift */,
D260105423CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift */,
012CA99D2385A2D3003F810F /* MFView+ModelExtension.swift */,
0A9F3DE723EDE9F200318918 /* Arrow.swift */,
0A9F3DE923EDEA1A00318918 /* ArrowModel.swift */,
);
path = Views;
sourceTree = "<group>";
@ -1688,6 +1694,7 @@
D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */,
D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */,
D282AACB2243C61700C46919 /* ButtonView.swift in Sources */,
0A9F3DE823EDE9F200318918 /* Arrow.swift in Sources */,
D260105D23D0BCD400764D80 /* Stack.swift in Sources */,
0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */,
D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */,
@ -1773,9 +1780,11 @@
943784F6236B77BB006A1E82 /* GraphViewAnimationHandler.swift in Sources */,
D29DF2AA21E7B2F9003B2FB9 /* MVMCoreUIConstants.m in Sources */,
948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */,
0A9F3DEA23EDEA1A00318918 /* ArrowModel.swift in Sources */,
D2A5146122121FBF00345BFB /* MoleculeStackTemplate.swift in Sources */,
D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */,
D29DF11821E6805F003B2FB9 /* NSLayoutConstraint+MFConvenience.m in Sources */,
0AB2AA2323F19CFA00C6D3CF /* BarsIndicatorView.swift in Sources */,
94C2D9A323872C110006CF46 /* LabelAttributeStrikeThroughModel.swift in Sources */,
D28A838523CCCA8900DFE4FC /* ScrollerModel.swift in Sources */,
D29DF26C21E6AA0B003B2FB9 /* FLAnimatedImage.m in Sources */,
@ -1798,7 +1807,6 @@
D2B18B922361E65A00A9AEDC /* CoreUIObject.swift in Sources */,
D29DF2BE21E7BEA4003B2FB9 /* TopTabbar.m in Sources */,
014AA72E23C5059B006F3E93 /* StackCenteredPageTemplateModel.swift in Sources */,
0A14F6B223E8C28D00EDF7F7 /* BarsIndicatorView.swift in Sources */,
D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */,
D260105923D0A92900764D80 /* ContainerProtocol.swift in Sources */,
C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */,

View File

@ -0,0 +1,24 @@
//
// Arrow.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 2/7/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
open class Arrow: View {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public var color: Color?
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
}

View File

@ -0,0 +1,61 @@
//
// ArrowModel.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 2/7/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import Foundation
public class ArrowModel: MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public var moleculeName: String?
public var backgroundColor: Color?
public static var identifier: String {
return "arrow"
}
public var enabledColor: Color?
public var disabledColor: Color?
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
case enabledColor
case disabledColor
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
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)
disabledColor = try typeContainer.decodeIfPresent(Color.self, forKey: .disabledColor)
enabledColor = try typeContainer.decodeIfPresent(Color.self, forKey: .enabledColor)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(enabledColor, forKey: .enabledColor)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(disabledColor, forKey: .disabledColor)
}
}

View File

@ -9,13 +9,19 @@
import Foundation
public protocol IndicatorViewProtocol {
func updateUI(oldIndex: Int, newIndex: Int)
func updateUI(previousIndex: Int, newIndex: Int, totalCount: Int, isAnimated: Bool)
func reset()
var isEnabled: Bool { get set }
var currentIndex: Int? { get set }
var numberOfPages: Int? { get }
}
open class CarouselIndicator: Control {
//--------------------------------------------------
// MARK: - Outlets
//--------------------------------------------------
public typealias IndicatorView = UIView & IndicatorViewProtocol
//--------------------------------------------------
// MARK: - Constraints
//--------------------------------------------------
@ -27,8 +33,6 @@ open class CarouselIndicator: Control {
// MARK: - Properties
//--------------------------------------------------
public typealias IndicatorView = UIView & IndicatorViewProtocol
/// The types of indicators that can appear.
public enum IndicatorType: String {
case bar
@ -38,47 +42,42 @@ open class CarouselIndicator: Control {
/// Determines interactivity and appearance of the indicator.
public var indicatorType: IndicatorType = .hybrid {
didSet {
assignIndicatorView()
}
didSet { assignIndicatorView() }
}
/// The currently active indicator view.
public var currentIndicator: IndicatorView?
public var carouselIndicatorModel: CarouselIndicatorModel? {
return model as? CarouselIndicatorModel
}
/// The view control relative to the state of the indicator type.
private(set) var indicatorView: IndicatorView? {
didSet {
topConstraint = indicatorView!.topAnchor.constraint(equalTo: topAnchor, constant: PaddingTwo)
topConstraint = indicatorView?.topAnchor.constraint(equalTo: topAnchor, constant: PaddingTwo)
topConstraint?.isActive = true
bottomConstraint = bottomAnchor.constraint(equalTo: indicatorView!.bottomAnchor, constant: PaddingTwo)
bottomConstraint?.isActive = true
if let indicatorView = indicatorView {
bottomConstraint = bottomAnchor.constraint(equalTo: indicatorView.bottomAnchor, constant: PaddingTwo)
bottomConstraint?.isActive = true
}
}
}
/// The maxmum count of pages before the indicatorView forces a numeric Indicator insead of Bar.
public var hybridThreshold: Int = 5
/// Spacing used between bars of the Bars Indicator and between the title and arrows of the Numeric Indicator
public var indicatorBarSpacing: CGFloat = 6 {
didSet {
if let stackView = indicatorView as? StackView {
stackView.spacing = indicatorBarSpacing
stackView.layoutIfNeeded()
}
}
}
// private(set) var indicatorBars = [BarIndicator]()
/// Set this closure to perform an action when a different indicator was selected.
public var indicatorTappedBlock: ((Int)->())?
public var indicatorTouchAction: ((Int)->())?
/// Allows sendActions() to trigger even if index is min/max index.
public var alwaysSendControlEvent = false
public var alwaysSendEvent = false
/// Set true to make the accessibility value as "Slide #currentPage of #totalPage", otherwise will be "Page #currentPage of #totalPage", default is false
public var isSlidesAccessibile = false
public var accessibilityHasSlidesInsteadOfPage = false
public var isAnimated = false
public var isAnimated = true
/// Will hide this control if page count is 1.
public var hidesForSinglePage = false {
@ -97,11 +96,7 @@ open class CarouselIndicator: Control {
} else {
if let stackView = indicatorView as? BarsIndicatorView {
stackView.stackView.arrangedSubviews.forEach { view in
// if let indicator = {
(view as? BarsIndicatorView)?.isEnabled = isEnabled
// }
}
stackView.stackView.arrangedSubviews.forEach { ($0 as? BarsIndicatorView)?.isEnabled = isEnabled }
}
}
}
@ -111,25 +106,22 @@ open class CarouselIndicator: Control {
// MARK: - Computed Properties
//--------------------------------------------------
/// The currently active indicator view.
public weak var currentIndicator: IndicatorView? {
didSet {
}
}
private(set) var oldIndex = 0
private(set) var previousIndex = 0
private var _currentIndex = 0
public var currentIndex: Int {
get { return _currentIndex }
set (newIndex) {
guard _currentIndex != newIndex else { return }
oldIndex = _currentIndex
previousIndex = _currentIndex
_currentIndex = newIndex
sendActions(for: .valueChanged)
indicatorTappedBlock?(newIndex)
indicatorView?.updateUI(oldIndex: oldIndex, newIndex: newIndex)
indicatorTouchAction?(newIndex)
indicatorView?.updateUI(previousIndex: previousIndex,
newIndex: newIndex,
totalCount: numberOfPages,
isAnimated: isAnimated)
}
}
@ -137,44 +129,46 @@ open class CarouselIndicator: Control {
public var numberOfPages: Int {
get { return _numberOfPages }
set {
guard _numberOfPages != newValue else { return }
_numberOfPages = newValue
set (newTotal) {
guard _numberOfPages != newTotal else { return }
_numberOfPages = newTotal
if hidesForSinglePage && newValue <= 1 {
if hidesForSinglePage && newTotal <= 1 {
isHidden = true
} else {
isHidden = false
indicatorView = BarsIndicatorView(numberOfBars: numberOfPages)
indicatorView = BarsIndicatorView()
}
indicatorView?.updateUI(oldIndex: oldIndex, newIndex: currentIndex)
if alwaysSendEvent {
sendActions(for: .valueChanged)
}
indicatorView?.updateUI(previousIndex: previousIndex,
newIndex: currentIndex,
totalCount: newTotal,
isAnimated: isAnimated)
}
}
private var _indicatorTintColor: UIColor = .mvmCoolGray6
public var disabledIndicatorColor: UIColor = .mvmCoolGray3
private var _indicatorTintColor: UIColor = .black
public var indicatorTintColor: UIColor {
get { return _indicatorTintColor }
set {
_indicatorTintColor = newValue
if isBarIndicator(), let barsView = (indicatorView as? BarsIndicatorView)?.stackView.arrangedSubviews.isEmpty {
indicatorView = BarsIndicatorView(numberOfBars: numberOfPages)
}
(indicatorView as? BarsIndicatorView)?.stackView.arrangedSubviews.forEach { $0.backgroundColor = newValue}
set (newColor) {
_indicatorTintColor = newColor
}
}
private var _currentPageIndicatorTintColor: UIColor = .black
private var _currentIndicatorColor: UIColor = .black
public var currentPageIndicatorTintColor: UIColor {
get { return _currentPageIndicatorTintColor }
set {
_currentPageIndicatorTintColor = newValue
if ((indicatorView as? BarsIndicatorView)?.stackView.arrangedSubviews.isEmpty)! {
indicatorView = BarsIndicatorView(numberOfBars: numberOfPages)
}
currentIndicator?.backgroundColor = newValue
/// Colors the currently selected index, unique from other indicators
public var currentIndicatorColor: UIColor {
get { return _currentIndicatorColor }
set (newColor) {
_currentIndicatorColor = newColor
}
}
@ -213,45 +207,57 @@ open class CarouselIndicator: Control {
open override func setupView() {
super.setupView()
if indicatorView == nil {
assignIndicatorView()
if let accessibleValue = MVMCoreUIUtility.hardcodedString(withKey: isSlidesAccessibile ? "MVMCoreUIPageControlslides_currentpage_index" : "MVMCoreUIPageControl_currentpage_index") {
accessibilityValue = String(format: accessibleValue, currentIndex + 1, numberOfPages)
}
guard indicatorView == nil else { return }
assignIndicatorView()
if let accessibleValue = MVMCoreUIUtility.hardcodedString(withKey: accessibilityHasSlidesInsteadOfPage ? "MVMCoreUIPageControlslides_currentpage_index" : "MVMCoreUIPageControl_currentpage_index") {
accessibilityValue = String(format: accessibleValue, currentIndex + 1, numberOfPages)
}
}
open override func updateView(_ size: CGFloat) {
super.updateView(size)
//--------------------------------------------------
// MARK: - UITouch
//--------------------------------------------------
@objc func pageValueIncrement() {
currentIndex = min(currentIndex + 1, numberOfPages - 1)
}
@objc func pageValueDecrement() {
currentIndex = max(0, currentIndex - 1)
}
func indicatorTapped(_ tapGesture: UITapGestureRecognizer?) {
if isEnabled, let bars = (indicatorView as? BarsIndicatorView)?.barsReference {
let touchPoint_X = tapGesture?.location(in: self).x ?? 0.0
currentIndex = bars.firstIndex { $0.0.frame.maxX >= touchPoint_X && $0.0.frame.minX <= touchPoint_X } ?? 0
}
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
/// Sets the indicatorView based on the current indicatorType.
func assignIndicatorView() {
switch indicatorType {
case .bar:
indicatorView = BarsIndicatorView(numberOfBars: numberOfPages)
indicatorView = BarsIndicatorView()
case .numeric:
indicatorView = NumericIndicatorView()
case .hybrid:
indicatorView = numberOfPages >= hybridThreshold ? NumericIndicatorView() : BarsIndicatorView(numberOfBars: numberOfPages)
indicatorView = numberOfPages >= hybridThreshold ? NumericIndicatorView() : BarsIndicatorView()
}
}
/// Removes all indicators from their subview and then clears the holding array.
func removeIndicatorView() {
// indicatorBars.forEach { $0.removeFromSuperview() }
// indicatorBars = []
}
/// Convenience to determine if current view is displaying bars.
func isBarIndicator() -> Bool {
return indicatorType != .bar && numberOfPages > hybridThreshold
@ -266,11 +272,8 @@ open class CarouselIndicator: Control {
guard let model = model as? CarouselIndicatorModel else { return }
if let type = model.type, let indicator = IndicatorType(rawValue: type) {
indicatorType = indicator
}
// backgroundColor = model.backgroundColor?.uiColor
indicatorType = IndicatorType(rawValue: model.type ?? "") ?? .hybrid
backgroundColor = model.backgroundColor?.uiColor
// barsColor = model.barsColor
// pageIndicatorTintColor
// currentPageIndicatorTintColor
@ -290,11 +293,11 @@ open class CarouselIndicator: Control {
func accessibilityAdjust(toPage index: Int) {
if (index < numberOfPages && index >= 0) || alwaysSendControlEvent {
if (index < numberOfPages && index >= 0) || alwaysSendEvent {
isAnimated = false
currentIndex = index
sendActions(for: .valueChanged)
indicatorTappedBlock?(index)
indicatorTouchAction?(index)
}
}

View File

@ -22,7 +22,19 @@ public class CarouselIndicatorModel: MoleculeModelProtocol {
public var moleculeName: String?
public var type: String? = "hybrid"
public var hybridThreshold: Int = 5
public var barsColor: Color?
public var currentBarColor: Color?
public var currentIndex: Int? = 0
public var numberOfPages: Int? = 0
public var alwaysSendEvent: Bool? = false
public var isAnimated: Bool? = true
public var hidesForSinglePage: Bool? = false
public var accessibilityHasSlidesInsteadOfPage: Bool? = false
public var isEnabled: Bool? = false
public var disabledIndicatorColor: Color? = Color(uiColor: .mvmCoolGray3)
public var indicatorTintColor: Color? = Color(uiColor: .black)
public var currentIndicatorColor: Color? = Color(uiColor: .black)
//--------------------------------------------------
// MARK: - Keys
@ -33,6 +45,7 @@ public class CarouselIndicatorModel: MoleculeModelProtocol {
case backgroundColor
case type
case barsColor
case currentBarColor
}
//--------------------------------------------------
@ -41,11 +54,8 @@ public class CarouselIndicatorModel: MoleculeModelProtocol {
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)
currentBarColor = try typeContainer.decodeIfPresent(Color.self, forKey: .currentBarColor)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
barsColor = try typeContainer.decodeIfPresent(Color.self, forKey: .barsColor)
type = try typeContainer.decodeIfPresent(String.self, forKey: .type) ?? "hybrid"
@ -55,6 +65,7 @@ public class CarouselIndicatorModel: MoleculeModelProtocol {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
try container.encodeIfPresent(barsColor, forKey: .barsColor)
try container.encodeIfPresent(currentBarColor, forKey: .currentBarColor)
try container.encode(moleculeName, forKey: .moleculeName)
try container.encodeIfPresent(type, forKey: .type)
}

View File

@ -1,156 +0,0 @@
//
// BarIndicatorView.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 2/3/20.
// Copyright © 2020 Verizon Wireless. All rights reserved.
//
import UIKit
open class BarsIndicatorView: View, IndicatorViewProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
let stackView: StackView = {
let stackView = StackView()
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.spacing = PaddingOne
return stackView
}()
var barsReference: [(view: View, constraint: NSLayoutConstraint)] = []
// Dimensions are based on InVision Design Guidelines.
public static let indicatorBarWidth: CGFloat = 24
public static let indicatorBarHeight: (selected: CGFloat, unselected: CGFloat) = (selected: 4, unselected: 1)
public var enabledColor: UIColor = .black
public var disabledColor: UIColor = .mvmCoolGray3
private var oldIndex: Int = 0
/// Returns the currentIndex from its parent CarouselIndicator.
public var currentIndex: Int? {
get { return (superview as? CarouselIndicator)?.currentIndex }
set {
guard let newValue = newValue else { return }
(superview as? CarouselIndicator)?.currentIndex = newValue
}
}
/// Returns the numberOfPages count from its parent CarouselIndicator.
public var numberOfPages: Int? {
return (superview as? CarouselIndicator)?.numberOfPages
}
public var numberOfBars: Int = 0 {
didSet {
// TODO: Generate bars...
}
}
open var isEnabled: Bool = true {
didSet {
barsReference.forEach { view, heightConstraint in
view.backgroundColor = isEnabled ? enabledColor : disabledColor
}
}
}
open var isAnimated: Bool {
return (superview as? CarouselIndicator)?.isAnimated ?? true
}
//--------------------------------------------------
// MARK: - Initializers
//--------------------------------------------------
public init(numberOfBars: Int) {
super.init(frame: .zero)
self.numberOfBars = numberOfBars
}
public required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//--------------------------------------------------
// MARK: - Setup
//--------------------------------------------------
open override func setupView() {
super.setupView()
if subviews.isEmpty {
addSubview(stackView)
stackView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
stackView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor).isActive = true
trailingAnchor.constraint(lessThanOrEqualTo: stackView.trailingAnchor).isActive = true
clearBars()
let tapGesture = UITapGestureRecognizer()
tapGesture.addTarget(self, action: #selector(indicatorTapped(_:)))
addGestureRecognizer(tapGesture)
}
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
func generateBars() {
var bars = [(View, NSLayoutConstraint)]()
for i in 0..<numberOfBars {
let bar = View()
bar.widthAnchor.constraint(equalToConstant: BarsIndicatorView.indicatorBarWidth).isActive = true
bar.backgroundColor = enabledColor
// let barHeight = i == currentIndex ? indicatorBarWidth.indicatorBarHeight.selected : indicatorBarWidth.indicatorBarHeight.unselected
// let heightConstraint = bar.heightAnchor.constraint(equalToConstant: barHeight)
// heightConstraint.isActive = true
// stackView.app
// bars.append((bar, heightConstraint))
}
barsReference = bars
}
func clearBars() {
barsReference.forEach { $0.view.removeFromSuperview() }
barsReference = []
}
func indicatorTapped(_ tapGesture: UITapGestureRecognizer?) {
if isEnabled {
let touchPoint_X = tapGesture?.location(in: self).x ?? 0.0
currentIndex = barsReference.firstIndex { view, _ in
return view.frame.maxX >= touchPoint_X && view.frame.minX <= touchPoint_X
}
}
}
//--------------------------------------------------
// MARK: - IndicatorViewProtocol
//--------------------------------------------------
public func updateUI(oldIndex: Int, newIndex: Int) {
let expression = {
self.barsReference[oldIndex].constraint.constant = BarsIndicatorView.indicatorBarHeight.unselected
self.barsReference[newIndex].constraint.constant = BarsIndicatorView.indicatorBarHeight.selected
self.layoutIfNeeded()
}
isAnimated ? UIView.animate(withDuration: 0.3) { expression() } : expression()
}
}

View File

@ -22,24 +22,24 @@ open class NumericIndicatorView: View, IndicatorViewProtocol {
return label
}()
// Left and right arrows for Numeric indicator
open var leftArrow = MFLoadImageView()
open var rightArrow = MFLoadImageView()
open var isEnabled: Bool = true
public var parentCarouselIndicator: CarouselIndicator? {
return superview as? CarouselIndicator
}
/// Returns the currentIndex from its parent CarouselIndicator.
public var currentIndex: Int? {
get { return (superview as? CarouselIndicator)?.currentIndex }
get { return parentCarouselIndicator?.currentIndex }
set {
guard let newValue = newValue else { return }
(superview as? CarouselIndicator)?.currentIndex = newValue
parentCarouselIndicator?.currentIndex = newValue
}
}
/// Returns the numberOfPages count from its parent CarouselIndicator.
public var numberOfPages: Int? {
return (superview as? CarouselIndicator)?.numberOfPages
return parentCarouselIndicator?.numberOfPages
}
//--------------------------------------------------
@ -76,37 +76,27 @@ open class NumericIndicatorView: View, IndicatorViewProtocol {
guard subviews.isEmpty else { return }
isUserInteractionEnabled = false
addSubview(titleLabel)
titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
NSLayoutConstraint.constraintPinSubview(titleLabel, pinTop: true, pinBottom: true, pinLeft: false, pinRight: false)
let arrow = UIImage(named: "peakingRightArrow")?.withHorizontallyFlippedOrientation()
let leftArrow = UIImageView(image: arrow)
leftArrow.isUserInteractionEnabled = true
addSubview(leftArrow)
NSLayoutConstraint.constraintPinView(leftArrow, heightConstraint: true, heightConstant: PaddingTwo, widthConstraint: true, widthConstant: PaddingTwo)
leftArrow.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
leftArrow.isUserInteractionEnabled = true
leftArrow.loadImage(withName: "peakingRightArrow", width: nil, height: nil) { [weak self] image, _, _ in
DispatchQueue.main.async { [weak self] in
guard let image = image else { return }
self?.leftArrow.imageView.image = image.withHorizontallyFlippedOrientation()
self?.leftArrow.layoutIfNeeded()
}
}
let leftTap = UITapGestureRecognizer()
leftTap.addTarget(self, action: #selector(pageValueDecrement))
leftArrow.addGestureRecognizer(leftTap)
rightArrow.loadImage(withName: "peakingRightArrow")
let rightArrow = UIImageView(image: UIImage(named: "peakingRightArrow"))
addSubview(rightArrow)
NSLayoutConstraint.constraintPinView(rightArrow, heightConstraint: true, heightConstant: PaddingTwo, widthConstraint: true, widthConstant: PaddingTwo)
rightArrow.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
rightArrow.isUserInteractionEnabled = true
let rightTap = UITapGestureRecognizer()
rightTap.addTarget(self, action: #selector(pageValueIncrement))
rightArrow.addGestureRecognizer(rightTap)
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[leftArrowView]-(padding)-[titleLabel]-(padding)-[rightArrowView]-0-|",
options: .directionLeadingToTrailing,
metrics: ["padding": PaddingOne],
@ -115,36 +105,17 @@ open class NumericIndicatorView: View, IndicatorViewProtocol {
"rightArrowView": rightArrow]))
}
//--------------------------------------------------
// MARK: - Actions
//--------------------------------------------------
@objc func pageValueIncrement() {
guard let currentIndex = currentIndex,
let numberOfPages = numberOfPages
else { return }
self.currentIndex = min(currentIndex + 1, numberOfPages - 1)
}
@objc func pageValueDecrement() {
guard let currentIndex = currentIndex else { return }
self.currentIndex = max(0, currentIndex - 1)
}
//--------------------------------------------------
// MARK: - IndicatorViewProtocol
//--------------------------------------------------
open func updateUI(oldIndex: Int, newIndex: Int) {
open func updateUI(previousIndex oldIndex: Int, newIndex: Int, totalCount: Int, isAnimated: Bool) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.titleLabel.text = "\(newIndex)/\(self.numberOfPages ?? 0)"
self.titleLabel.text = "\(newIndex)/\(totalCount)"
self.layoutIfNeeded()
(self.superview as? CarouselIndicator)?.sendActions(for: .valueChanged)
}
}
}

View File

@ -8,6 +8,7 @@
import Foundation
public protocol CarouselPagingModelProtocol: MoleculeModelProtocol {
var position: Float? {get}
var position: Float? { get }
}

View File

@ -10,12 +10,20 @@ import Foundation
@objcMembers public class CarouselItemModel: MoleculeContainerModel, CarouselItemModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "carouselItem"
public var backgroundColor: Color?
public var peakingUI: Bool?
public var peakingArrowColor: Color?
public var moleculeName: String?
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
@ -23,6 +31,10 @@ import Foundation
case peakingArrowColor
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
moleculeName = try typeContainer.decodeIfPresent(String.self, forKey: .moleculeName)

View File

@ -9,7 +9,7 @@
import UIKit
open class Carousel: View {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
/// The current index of the collection view. Includes dummy cells when looping.
@ -17,10 +17,8 @@ open class Carousel: View {
/// The index of the page, does not include dummy cells.
var pageIndex: Int {
get {
return loop ? currentIndex - 2 : currentIndex
}
set(newIndex) {
get { return loop ? currentIndex - 2 : currentIndex }
set (newIndex) {
currentIndex = loop ? newIndex + 2 : newIndex
}
}
@ -30,7 +28,7 @@ open class Carousel: View {
/// The json for the molecules.
var molecules: [MoleculeModelProtocol]?
/// The horizontal alignment of the cell in the collection view. Only noticeable if the itemWidthPercent is less than 100%.
var itemAlignment = UICollectionView.ScrollPosition.left
@ -53,9 +51,8 @@ open class Carousel: View {
// MARK: - MVMCoreViewProtocol
open override func setupView() {
super.setupView()
guard collectionView.superview == nil else {
return
}
guard collectionView.superview == nil else { return }
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.dataSource = self
collectionView.delegate = self
@ -73,7 +70,7 @@ open class Carousel: View {
super.updateView(size)
collectionView.collectionViewLayout.invalidateLayout()
showPeaking(false)
// Go to current cell. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking logic. The dispatch is a sad way to ensure the collection view is ready to be scrolled.
DispatchQueue.main.async {
self.collectionView.scrollToItem(at: IndexPath(row: self.currentIndex, section: 0), at: self.itemAlignment, animated: false)
@ -83,32 +80,35 @@ open class Carousel: View {
}
// MARK: - MVMCoreUIMoleculeViewProtocol
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
super.setWithModel(model, delegateObject, additionalData)
guard let carouselModel = model as? CarouselModel else { return }
collectionView.backgroundColor = backgroundColor
collectionView.layer.borderColor = backgroundColor?.cgColor
collectionView.layer.borderWidth = (carouselModel.border ?? false) ? 1 : 0
backgroundColor = .white
registerCells(with: carouselModel, delegateObject: delegateObject)
setupLayout(with: carouselModel)
prepareMolecules(with: carouselModel)
itemWidthPercent = (carouselModel.itemWidthPercent ?? 100) / 100
setAlignment(with: carouselModel.itemAlignment)
if let height = carouselModel.height {
collectionViewHeight?.constant = CGFloat(height)
collectionViewHeight?.isActive = true
}
setupPagingMolecule(carouselModel.pagingMolecule, delegateObject: delegateObject)
collectionView.reloadData()
}
// MARK: - JSON Setters
/// Updates the layout being used
func setupLayout(with carouselModel: CarouselModel?) {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
@ -116,14 +116,14 @@ open class Carousel: View {
layout.minimumInteritemSpacing = 0
collectionView.collectionViewLayout = layout
}
func prepareMolecules(with carouselModel: CarouselModel?) {
guard let newMolecules = carouselModel?.molecules else {
numberOfPages = 0
molecules = nil
return
}
numberOfPages = newMolecules.count
molecules = newMolecules
if carouselModel?.loop ?? false && newMolecules.count > 2 {
@ -136,16 +136,17 @@ open class Carousel: View {
}
pageIndex = 0
}
/// Sets up the paging molecule
open func setupPagingMolecule(_ molecule: CarouselPagingModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) {
var pagingView: (UIView & MVMCoreUIPagingProtocol)? = nil
if let molecule = molecule {
pagingView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(molecule, delegateObject, false) as? (UIView & MVMCoreUIPagingProtocol)
}
addPaging(view: pagingView, position: (CGFloat(molecule?.position ?? 20)))
}
/// Registers the cells with the collection view
func registerCells(with carouselModel: CarouselModel, delegateObject: MVMCoreUIDelegateObject?) {
for molecule in carouselModel.molecules {
@ -154,19 +155,20 @@ open class Carousel: View {
}
}
}
// MARK: - Convenience
/// Returns the (identifier, class) of the molecule for the given map.
func getMoleculeInfo(with molecule: MoleculeModelProtocol, delegateObject: MVMCoreUIDelegateObject?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? {
guard let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(molecule) ,
let moleculeName = (className as? ModelMoleculeViewProtocol.Type)?.nameForReuse(molecule, delegateObject) ?? molecule.moleculeName else {
return nil
}
guard let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(molecule),
let moleculeName = (className as? ModelMoleculeViewProtocol.Type)?.nameForReuse(molecule, delegateObject) ?? molecule.moleculeName
else { return nil }
return (moleculeName, className, molecule)
}
/// Sets the alignment from the string.
open func setAlignment(with string: String?) {
switch string {
case "leading":
itemAlignment = .left
@ -213,6 +215,7 @@ open class Carousel: View {
}
open func showPeaking(_ peaking: Bool) {
if peaking && !UIAccessibility.isVoiceOverRunning {
// Show overlay and arrow in peaking Cell
let visibleItemsPaths = collectionView.indexPathsForVisibleItems.sorted { $0.row < $1.row }
@ -231,9 +234,8 @@ open class Carousel: View {
}
func setAccessiblity(_ cell: UICollectionViewCell?, index: Int) {
guard let cell = cell else {
return
}
guard let cell = cell else { return }
if index == currentIndex {
cell.accessibilityElementsHidden = false
var array = cell.accessibilityElements
@ -269,9 +271,9 @@ extension Carousel: UICollectionViewDataSource {
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let molecule = molecules?[indexPath.row],
let moleculeInfo = getMoleculeInfo(with: molecule, delegateObject: nil) else {
return UICollectionViewCell()
}
let moleculeInfo = getMoleculeInfo(with: molecule, delegateObject: nil)
else { return UICollectionViewCell() }
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: moleculeInfo.identifier, for: indexPath)
if let protocolCell = cell as? MVMCoreUIMoleculeViewProtocol & ModelMoleculeViewProtocol {
protocolCell.reset?()
@ -286,6 +288,7 @@ extension Carousel: UICollectionViewDataSource {
extension Carousel: UIScrollViewDelegate {
func goTo(_ index: Int, animated: Bool) {
showPeaking(false)
setAccessiblity(collectionView.cellForItem(at: IndexPath(row: self.currentIndex, section: 0)), index: index)
self.currentIndex = index
@ -297,17 +300,15 @@ extension Carousel: UIScrollViewDelegate {
}
func handleUserOnBufferCell() {
guard loop else {
return
}
guard loop else { return }
let lastPageIndex = numberOfPages + 1
let goToIndex = {(index: Int) in
self.goTo(index, animated: false)
self.collectionView.layoutIfNeeded()
self.pagingView?.setPage(self.pageIndex)
}
if currentIndex < 2 {
// If on a "buffer" last row (which is the first index), go to the real last row secretly. layoutIfNeeded is needed otherwise cellForItem returns nil for peaking.
goToIndex(lastPageIndex)
@ -318,9 +319,8 @@ extension Carousel: UIScrollViewDelegate {
}
func checkForDraggingOutOfBounds(_ scrollView: UIScrollView) {
guard loop, dragging else {
return
}
guard loop, dragging else { return }
// Checks if the user is not paging but attempting to drag endlessly and goes out of bounds. Caps the index.
if let separatorWidth = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing {
let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent)
@ -346,24 +346,25 @@ extension Carousel: UIScrollViewDelegate {
}
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
dragging = true
showPeaking(false)
}
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
dragging = false
targetContentOffset.pointee = scrollView.contentOffset
// This is for setting up smooth custom paging. (Since UICollectionView only handles paging based on collection view size and not cell size).
guard let separatorWidth = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing else {
return
}
guard let separatorWidth = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing else { return }
// We switch cards if we pass the velocity threshold or position threshold (currently 50%).
let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent)
var cellToSwipeTo = Int(scrollView.contentOffset.x/(itemWidth + separatorWidth) + 0.5)
let lastCellIndex = collectionView(collectionView, numberOfItemsInSection: 0) - 1
let velocityThreshold: CGFloat = 1.1
if velocity.x > velocityThreshold {
cellToSwipeTo = currentIndex + 1
} else if velocity.x < -velocityThreshold {

View File

@ -9,10 +9,14 @@
import UIKit
@objcMembers public class CarouselModel: MoleculeModelProtocol {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
public static var identifier: String = "carousel"
public var backgroundColor: Color?
public var molecules: [CarouselItemModel]
public var moleculeName: String?
public var spacing: Float?
public var border: Bool?
public var loop: Bool?
@ -20,12 +24,20 @@ import UIKit
public var itemWidthPercent: Float?
public var itemAlignment: String?
public var pagingMolecule: CarouselPagingModelProtocol?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public init(molecules: [CarouselItemModel]){
self.molecules = molecules
}
private enum CodingKeys: String, CodingKey {
//--------------------------------------------------
// MARK: - Keys
//--------------------------------------------------
private enum CodingKeys: String, CodingKey {
case moleculeName
case backgroundColor
case molecules
@ -36,32 +48,36 @@ import UIKit
case itemWidthPercent
case itemAlignment
case pagingMolecule
}
required public init(from decoder: Decoder) throws {
}
//--------------------------------------------------
// MARK: - Codec
//--------------------------------------------------
required public init(from decoder: Decoder) throws {
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
self.molecules = try typeContainer.decode([CarouselItemModel].self, forKey: .molecules)
self.backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
self.spacing = try typeContainer.decode(Float.self, forKey: .spacing)
self.border = try typeContainer.decode(Bool.self, forKey: .border)
self.loop = try typeContainer.decode(Bool.self, forKey: .loop)
self.height = try typeContainer.decode(Float.self, forKey: .height)
self.itemWidthPercent = try typeContainer.decode(Float.self, forKey: .itemWidthPercent)
self.itemAlignment = try typeContainer.decode(String.self, forKey: .itemAlignment)
self.pagingMolecule = try typeContainer.decodeMoleculeIfPresent(codingKey: .pagingMolecule) as? CarouselPagingModelProtocol
}
public func encode(to encoder: Encoder) throws {
molecules = try typeContainer.decode([CarouselItemModel].self, forKey: .molecules)
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
spacing = try typeContainer.decodeIfPresent(Float.self, forKey: .spacing)
border = try typeContainer.decodeIfPresent(Bool.self, forKey: .border)
loop = try typeContainer.decodeIfPresent(Bool.self, forKey: .loop)
height = try typeContainer.decodeIfPresent(Float.self, forKey: .height)
itemWidthPercent = try typeContainer.decodeIfPresent(Float.self, forKey: .itemWidthPercent)
itemAlignment = try typeContainer.decodeIfPresent(String.self, forKey: .itemAlignment)
pagingMolecule = try typeContainer.decodeMoleculeIfPresent(codingKey: .pagingMolecule) as? CarouselPagingModelProtocol
}
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.encode(molecules, forKey: .molecules)
try container.encode(spacing, forKey: .spacing)
try container.encode(border, forKey: .border)
try container.encode(loop, forKey: .loop)
try container.encode(height, forKey: .height)
try container.encode(itemWidthPercent, forKey: .itemWidthPercent)
try container.encode(itemAlignment, forKey: .itemAlignment)
try container.encodeIfPresent(molecules, forKey: .molecules)
try container.encodeIfPresent(spacing, forKey: .spacing)
try container.encodeIfPresent(border, forKey: .border)
try container.encodeIfPresent(loop, forKey: .loop)
try container.encodeIfPresent(height, forKey: .height)
try container.encodeIfPresent(itemWidthPercent, forKey: .itemWidthPercent)
try container.encodeIfPresent(itemAlignment, forKey: .itemAlignment)
try container.encodeModelIfPresent(pagingMolecule, forKey: .pagingMolecule)
}
}
}

View File

@ -94,6 +94,8 @@ import Foundation
// Other Organisms
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Carousel.self, viewModelClass: CarouselModel.self)
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: CarouselIndicator.self, viewModelClass: CarouselIndicatorModel.self)
// TODO: Need model
MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping.setObject(DigitEntryField.self, forKey: "digitTextField" as NSString)
MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping.setObject(DateDropdownEntryField.self, forKey: "dateDropdownEntryField" as NSString)