work in progress
This commit is contained in:
parent
4577274e08
commit
fd72f5ff95
@ -63,7 +63,6 @@
|
|||||||
0A14F69323E349EF00EDF7F7 /* CarouselIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A14F69223E349EF00EDF7F7 /* CarouselIndicator.swift */; };
|
0A14F69323E349EF00EDF7F7 /* CarouselIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A14F69223E349EF00EDF7F7 /* CarouselIndicator.swift */; };
|
||||||
0A14F6A523E4803A00EDF7F7 /* StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A14F6A423E4803A00EDF7F7 /* StackView.swift */; };
|
0A14F6A523E4803A00EDF7F7 /* StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A14F6A423E4803A00EDF7F7 /* StackView.swift */; };
|
||||||
0A14F6A923E8750300EDF7F7 /* CarouselIndicatorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A14F6A823E8750300EDF7F7 /* CarouselIndicatorModel.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 */; };
|
0A14F6B423E8C29700EDF7F7 /* NumericIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A14F6B323E8C29700EDF7F7 /* NumericIndicatorView.swift */; };
|
||||||
0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxLabel.swift */; };
|
0A1B4A96233BB18F005B3FB4 /* CheckboxLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxLabel.swift */; };
|
||||||
0A21DB7F235DECC500C160A2 /* EntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A21DB7E235DECC500C160A2 /* EntryField.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 */; };
|
0A7EF86323D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86223D8AFA000B2AAD1 /* BaseDropdownEntryFieldModel.swift */; };
|
||||||
0A7EF86523D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */; };
|
0A7EF86523D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86423D8AFFF00B2AAD1 /* ItemDropdownEntryFieldModel.swift */; };
|
||||||
0A7EF86723D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7EF86623D8B0AE00B2AAD1 /* DateDropdownEntryFieldModel.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 */; };
|
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 */; };
|
0ABD136D237CAD1E0081388D /* DateDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */; };
|
||||||
0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */; };
|
0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */; };
|
||||||
0AE14F64238315D2005417F8 /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE14F63238315D2005417F8 /* TextField.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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
0AE14F63238315D2005417F8 /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = "<group>"; };
|
||||||
@ -742,7 +746,7 @@
|
|||||||
0A14F6B723ECA7F900EDF7F7 /* IndicatorViews */ = {
|
0A14F6B723ECA7F900EDF7F7 /* IndicatorViews */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0A14F6B123E8C28D00EDF7F7 /* BarsIndicatorView.swift */,
|
0AB2AA2223F19CFA00C6D3CF /* BarsIndicatorView.swift */,
|
||||||
0A14F6B323E8C29700EDF7F7 /* NumericIndicatorView.swift */,
|
0A14F6B323E8C29700EDF7F7 /* NumericIndicatorView.swift */,
|
||||||
);
|
);
|
||||||
path = IndicatorViews;
|
path = IndicatorViews;
|
||||||
@ -1241,6 +1245,8 @@
|
|||||||
0AA33B392398524F0067DD0F /* Toggle.swift */,
|
0AA33B392398524F0067DD0F /* Toggle.swift */,
|
||||||
D260105423CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift */,
|
D260105423CEA7DC00764D80 /* MVMCoreUISwitch+Model.swift */,
|
||||||
012CA99D2385A2D3003F810F /* MFView+ModelExtension.swift */,
|
012CA99D2385A2D3003F810F /* MFView+ModelExtension.swift */,
|
||||||
|
0A9F3DE723EDE9F200318918 /* Arrow.swift */,
|
||||||
|
0A9F3DE923EDEA1A00318918 /* ArrowModel.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1688,6 +1694,7 @@
|
|||||||
D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */,
|
D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */,
|
||||||
D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */,
|
D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */,
|
||||||
D282AACB2243C61700C46919 /* ButtonView.swift in Sources */,
|
D282AACB2243C61700C46919 /* ButtonView.swift in Sources */,
|
||||||
|
0A9F3DE823EDE9F200318918 /* Arrow.swift in Sources */,
|
||||||
D260105D23D0BCD400764D80 /* Stack.swift in Sources */,
|
D260105D23D0BCD400764D80 /* Stack.swift in Sources */,
|
||||||
0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */,
|
0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */,
|
||||||
D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */,
|
D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */,
|
||||||
@ -1773,9 +1780,11 @@
|
|||||||
943784F6236B77BB006A1E82 /* GraphViewAnimationHandler.swift in Sources */,
|
943784F6236B77BB006A1E82 /* GraphViewAnimationHandler.swift in Sources */,
|
||||||
D29DF2AA21E7B2F9003B2FB9 /* MVMCoreUIConstants.m in Sources */,
|
D29DF2AA21E7B2F9003B2FB9 /* MVMCoreUIConstants.m in Sources */,
|
||||||
948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */,
|
948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */,
|
||||||
|
0A9F3DEA23EDEA1A00318918 /* ArrowModel.swift in Sources */,
|
||||||
D2A5146122121FBF00345BFB /* MoleculeStackTemplate.swift in Sources */,
|
D2A5146122121FBF00345BFB /* MoleculeStackTemplate.swift in Sources */,
|
||||||
D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */,
|
D2E2A9A323E096B1000B42E6 /* DisableableModelProtocol.swift in Sources */,
|
||||||
D29DF11821E6805F003B2FB9 /* NSLayoutConstraint+MFConvenience.m in Sources */,
|
D29DF11821E6805F003B2FB9 /* NSLayoutConstraint+MFConvenience.m in Sources */,
|
||||||
|
0AB2AA2323F19CFA00C6D3CF /* BarsIndicatorView.swift in Sources */,
|
||||||
94C2D9A323872C110006CF46 /* LabelAttributeStrikeThroughModel.swift in Sources */,
|
94C2D9A323872C110006CF46 /* LabelAttributeStrikeThroughModel.swift in Sources */,
|
||||||
D28A838523CCCA8900DFE4FC /* ScrollerModel.swift in Sources */,
|
D28A838523CCCA8900DFE4FC /* ScrollerModel.swift in Sources */,
|
||||||
D29DF26C21E6AA0B003B2FB9 /* FLAnimatedImage.m in Sources */,
|
D29DF26C21E6AA0B003B2FB9 /* FLAnimatedImage.m in Sources */,
|
||||||
@ -1798,7 +1807,6 @@
|
|||||||
D2B18B922361E65A00A9AEDC /* CoreUIObject.swift in Sources */,
|
D2B18B922361E65A00A9AEDC /* CoreUIObject.swift in Sources */,
|
||||||
D29DF2BE21E7BEA4003B2FB9 /* TopTabbar.m in Sources */,
|
D29DF2BE21E7BEA4003B2FB9 /* TopTabbar.m in Sources */,
|
||||||
014AA72E23C5059B006F3E93 /* StackCenteredPageTemplateModel.swift in Sources */,
|
014AA72E23C5059B006F3E93 /* StackCenteredPageTemplateModel.swift in Sources */,
|
||||||
0A14F6B223E8C28D00EDF7F7 /* BarsIndicatorView.swift in Sources */,
|
|
||||||
D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */,
|
D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */,
|
||||||
D260105923D0A92900764D80 /* ContainerProtocol.swift in Sources */,
|
D260105923D0A92900764D80 /* ContainerProtocol.swift in Sources */,
|
||||||
C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */,
|
C695A69423C9909000BFB94E /* DoughnutChartModel.swift in Sources */,
|
||||||
|
|||||||
24
MVMCoreUI/Atoms/Views/Arrow.swift
Normal file
24
MVMCoreUI/Atoms/Views/Arrow.swift
Normal 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
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
61
MVMCoreUI/Atoms/Views/ArrowModel.swift
Normal file
61
MVMCoreUI/Atoms/Views/ArrowModel.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,13 +9,19 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public protocol IndicatorViewProtocol {
|
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 isEnabled: Bool { get set }
|
||||||
var currentIndex: Int? { get set }
|
|
||||||
var numberOfPages: Int? { get }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
open class CarouselIndicator: Control {
|
open class CarouselIndicator: Control {
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Outlets
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
public typealias IndicatorView = UIView & IndicatorViewProtocol
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Constraints
|
// MARK: - Constraints
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -27,8 +33,6 @@ open class CarouselIndicator: Control {
|
|||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public typealias IndicatorView = UIView & IndicatorViewProtocol
|
|
||||||
|
|
||||||
/// The types of indicators that can appear.
|
/// The types of indicators that can appear.
|
||||||
public enum IndicatorType: String {
|
public enum IndicatorType: String {
|
||||||
case bar
|
case bar
|
||||||
@ -38,47 +42,42 @@ open class CarouselIndicator: Control {
|
|||||||
|
|
||||||
/// Determines interactivity and appearance of the indicator.
|
/// Determines interactivity and appearance of the indicator.
|
||||||
public var indicatorType: IndicatorType = .hybrid {
|
public var indicatorType: IndicatorType = .hybrid {
|
||||||
didSet {
|
didSet { assignIndicatorView() }
|
||||||
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.
|
/// The view control relative to the state of the indicator type.
|
||||||
private(set) var indicatorView: IndicatorView? {
|
private(set) var indicatorView: IndicatorView? {
|
||||||
didSet {
|
didSet {
|
||||||
topConstraint = indicatorView!.topAnchor.constraint(equalTo: topAnchor, constant: PaddingTwo)
|
topConstraint = indicatorView?.topAnchor.constraint(equalTo: topAnchor, constant: PaddingTwo)
|
||||||
topConstraint?.isActive = true
|
topConstraint?.isActive = true
|
||||||
|
|
||||||
bottomConstraint = bottomAnchor.constraint(equalTo: indicatorView!.bottomAnchor, constant: PaddingTwo)
|
if let indicatorView = indicatorView {
|
||||||
bottomConstraint?.isActive = true
|
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.
|
/// The maxmum count of pages before the indicatorView forces a numeric Indicator insead of Bar.
|
||||||
public var hybridThreshold: Int = 5
|
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.
|
/// 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.
|
/// 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
|
/// 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.
|
/// Will hide this control if page count is 1.
|
||||||
public var hidesForSinglePage = false {
|
public var hidesForSinglePage = false {
|
||||||
@ -97,11 +96,7 @@ open class CarouselIndicator: Control {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
if let stackView = indicatorView as? BarsIndicatorView {
|
if let stackView = indicatorView as? BarsIndicatorView {
|
||||||
stackView.stackView.arrangedSubviews.forEach { view in
|
stackView.stackView.arrangedSubviews.forEach { ($0 as? BarsIndicatorView)?.isEnabled = isEnabled }
|
||||||
// if let indicator = {
|
|
||||||
(view as? BarsIndicatorView)?.isEnabled = isEnabled
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,25 +106,22 @@ open class CarouselIndicator: Control {
|
|||||||
// MARK: - Computed Properties
|
// MARK: - Computed Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
/// The currently active indicator view.
|
private(set) var previousIndex = 0
|
||||||
public weak var currentIndicator: IndicatorView? {
|
|
||||||
didSet {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private(set) var oldIndex = 0
|
|
||||||
private var _currentIndex = 0
|
private var _currentIndex = 0
|
||||||
|
|
||||||
public var currentIndex: Int {
|
public var currentIndex: Int {
|
||||||
get { return _currentIndex }
|
get { return _currentIndex }
|
||||||
set (newIndex) {
|
set (newIndex) {
|
||||||
guard _currentIndex != newIndex else { return }
|
guard _currentIndex != newIndex else { return }
|
||||||
oldIndex = _currentIndex
|
|
||||||
|
previousIndex = _currentIndex
|
||||||
_currentIndex = newIndex
|
_currentIndex = newIndex
|
||||||
sendActions(for: .valueChanged)
|
sendActions(for: .valueChanged)
|
||||||
indicatorTappedBlock?(newIndex)
|
indicatorTouchAction?(newIndex)
|
||||||
indicatorView?.updateUI(oldIndex: oldIndex, newIndex: newIndex)
|
indicatorView?.updateUI(previousIndex: previousIndex,
|
||||||
|
newIndex: newIndex,
|
||||||
|
totalCount: numberOfPages,
|
||||||
|
isAnimated: isAnimated)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,44 +129,46 @@ open class CarouselIndicator: Control {
|
|||||||
|
|
||||||
public var numberOfPages: Int {
|
public var numberOfPages: Int {
|
||||||
get { return _numberOfPages }
|
get { return _numberOfPages }
|
||||||
set {
|
set (newTotal) {
|
||||||
guard _numberOfPages != newValue else { return }
|
guard _numberOfPages != newTotal else { return }
|
||||||
_numberOfPages = newValue
|
_numberOfPages = newTotal
|
||||||
|
|
||||||
if hidesForSinglePage && newValue <= 1 {
|
if hidesForSinglePage && newTotal <= 1 {
|
||||||
isHidden = true
|
isHidden = true
|
||||||
} else {
|
} else {
|
||||||
isHidden = false
|
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 {
|
public var indicatorTintColor: UIColor {
|
||||||
get { return _indicatorTintColor }
|
get { return _indicatorTintColor }
|
||||||
set {
|
set (newColor) {
|
||||||
_indicatorTintColor = newValue
|
_indicatorTintColor = newColor
|
||||||
if isBarIndicator(), let barsView = (indicatorView as? BarsIndicatorView)?.stackView.arrangedSubviews.isEmpty {
|
|
||||||
indicatorView = BarsIndicatorView(numberOfBars: numberOfPages)
|
|
||||||
}
|
|
||||||
(indicatorView as? BarsIndicatorView)?.stackView.arrangedSubviews.forEach { $0.backgroundColor = newValue}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var _currentPageIndicatorTintColor: UIColor = .black
|
private var _currentIndicatorColor: UIColor = .black
|
||||||
|
|
||||||
public var currentPageIndicatorTintColor: UIColor {
|
/// Colors the currently selected index, unique from other indicators
|
||||||
get { return _currentPageIndicatorTintColor }
|
public var currentIndicatorColor: UIColor {
|
||||||
set {
|
get { return _currentIndicatorColor }
|
||||||
_currentPageIndicatorTintColor = newValue
|
set (newColor) {
|
||||||
if ((indicatorView as? BarsIndicatorView)?.stackView.arrangedSubviews.isEmpty)! {
|
_currentIndicatorColor = newColor
|
||||||
indicatorView = BarsIndicatorView(numberOfBars: numberOfPages)
|
|
||||||
}
|
|
||||||
currentIndicator?.backgroundColor = newValue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,45 +207,57 @@ open class CarouselIndicator: Control {
|
|||||||
open override func setupView() {
|
open override func setupView() {
|
||||||
super.setupView()
|
super.setupView()
|
||||||
|
|
||||||
if indicatorView == nil {
|
guard indicatorView == nil else { return }
|
||||||
|
|
||||||
assignIndicatorView()
|
assignIndicatorView()
|
||||||
|
|
||||||
if let accessibleValue = MVMCoreUIUtility.hardcodedString(withKey: isSlidesAccessibile ? "MVMCoreUIPageControlslides_currentpage_index" : "MVMCoreUIPageControl_currentpage_index") {
|
if let accessibleValue = MVMCoreUIUtility.hardcodedString(withKey: accessibilityHasSlidesInsteadOfPage ? "MVMCoreUIPageControlslides_currentpage_index" : "MVMCoreUIPageControl_currentpage_index") {
|
||||||
accessibilityValue = String(format: accessibleValue, currentIndex + 1, numberOfPages)
|
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
|
// MARK: - Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
/// Sets the indicatorView based on the current indicatorType.
|
||||||
func assignIndicatorView() {
|
func assignIndicatorView() {
|
||||||
|
|
||||||
switch indicatorType {
|
switch indicatorType {
|
||||||
case .bar:
|
case .bar:
|
||||||
indicatorView = BarsIndicatorView(numberOfBars: numberOfPages)
|
indicatorView = BarsIndicatorView()
|
||||||
|
|
||||||
case .numeric:
|
case .numeric:
|
||||||
indicatorView = NumericIndicatorView()
|
indicatorView = NumericIndicatorView()
|
||||||
|
|
||||||
case .hybrid:
|
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.
|
/// Convenience to determine if current view is displaying bars.
|
||||||
func isBarIndicator() -> Bool {
|
func isBarIndicator() -> Bool {
|
||||||
return indicatorType != .bar && numberOfPages > hybridThreshold
|
return indicatorType != .bar && numberOfPages > hybridThreshold
|
||||||
@ -266,11 +272,8 @@ open class CarouselIndicator: Control {
|
|||||||
|
|
||||||
guard let model = model as? CarouselIndicatorModel else { return }
|
guard let model = model as? CarouselIndicatorModel else { return }
|
||||||
|
|
||||||
if let type = model.type, let indicator = IndicatorType(rawValue: type) {
|
indicatorType = IndicatorType(rawValue: model.type ?? "") ?? .hybrid
|
||||||
indicatorType = indicator
|
backgroundColor = model.backgroundColor?.uiColor
|
||||||
}
|
|
||||||
|
|
||||||
// backgroundColor = model.backgroundColor?.uiColor
|
|
||||||
// barsColor = model.barsColor
|
// barsColor = model.barsColor
|
||||||
// pageIndicatorTintColor
|
// pageIndicatorTintColor
|
||||||
// currentPageIndicatorTintColor
|
// currentPageIndicatorTintColor
|
||||||
@ -290,11 +293,11 @@ open class CarouselIndicator: Control {
|
|||||||
|
|
||||||
func accessibilityAdjust(toPage index: Int) {
|
func accessibilityAdjust(toPage index: Int) {
|
||||||
|
|
||||||
if (index < numberOfPages && index >= 0) || alwaysSendControlEvent {
|
if (index < numberOfPages && index >= 0) || alwaysSendEvent {
|
||||||
isAnimated = false
|
isAnimated = false
|
||||||
currentIndex = index
|
currentIndex = index
|
||||||
sendActions(for: .valueChanged)
|
sendActions(for: .valueChanged)
|
||||||
indicatorTappedBlock?(index)
|
indicatorTouchAction?(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,19 @@ public class CarouselIndicatorModel: MoleculeModelProtocol {
|
|||||||
|
|
||||||
public var moleculeName: String?
|
public var moleculeName: String?
|
||||||
public var type: String? = "hybrid"
|
public var type: String? = "hybrid"
|
||||||
|
public var hybridThreshold: Int = 5
|
||||||
public var barsColor: Color?
|
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
|
// MARK: - Keys
|
||||||
@ -33,6 +45,7 @@ public class CarouselIndicatorModel: MoleculeModelProtocol {
|
|||||||
case backgroundColor
|
case backgroundColor
|
||||||
case type
|
case type
|
||||||
case barsColor
|
case barsColor
|
||||||
|
case currentBarColor
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -41,11 +54,8 @@ public class CarouselIndicatorModel: MoleculeModelProtocol {
|
|||||||
|
|
||||||
required public init(from decoder: Decoder) throws {
|
required public init(from decoder: Decoder) throws {
|
||||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
// if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) {
|
|
||||||
// self.state = state
|
currentBarColor = try typeContainer.decodeIfPresent(Color.self, forKey: .currentBarColor)
|
||||||
// }
|
|
||||||
// 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)
|
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||||
barsColor = try typeContainer.decodeIfPresent(Color.self, forKey: .barsColor)
|
barsColor = try typeContainer.decodeIfPresent(Color.self, forKey: .barsColor)
|
||||||
type = try typeContainer.decodeIfPresent(String.self, forKey: .type) ?? "hybrid"
|
type = try typeContainer.decodeIfPresent(String.self, forKey: .type) ?? "hybrid"
|
||||||
@ -55,6 +65,7 @@ public class CarouselIndicatorModel: MoleculeModelProtocol {
|
|||||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||||
try container.encodeIfPresent(barsColor, forKey: .barsColor)
|
try container.encodeIfPresent(barsColor, forKey: .barsColor)
|
||||||
|
try container.encodeIfPresent(currentBarColor, forKey: .currentBarColor)
|
||||||
try container.encode(moleculeName, forKey: .moleculeName)
|
try container.encode(moleculeName, forKey: .moleculeName)
|
||||||
try container.encodeIfPresent(type, forKey: .type)
|
try container.encodeIfPresent(type, forKey: .type)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -22,24 +22,24 @@ open class NumericIndicatorView: View, IndicatorViewProtocol {
|
|||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Left and right arrows for Numeric indicator
|
|
||||||
open var leftArrow = MFLoadImageView()
|
|
||||||
open var rightArrow = MFLoadImageView()
|
|
||||||
|
|
||||||
open var isEnabled: Bool = true
|
open var isEnabled: Bool = true
|
||||||
|
|
||||||
|
public var parentCarouselIndicator: CarouselIndicator? {
|
||||||
|
return superview as? CarouselIndicator
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the currentIndex from its parent CarouselIndicator.
|
/// Returns the currentIndex from its parent CarouselIndicator.
|
||||||
public var currentIndex: Int? {
|
public var currentIndex: Int? {
|
||||||
get { return (superview as? CarouselIndicator)?.currentIndex }
|
get { return parentCarouselIndicator?.currentIndex }
|
||||||
set {
|
set {
|
||||||
guard let newValue = newValue else { return }
|
guard let newValue = newValue else { return }
|
||||||
(superview as? CarouselIndicator)?.currentIndex = newValue
|
parentCarouselIndicator?.currentIndex = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the numberOfPages count from its parent CarouselIndicator.
|
/// Returns the numberOfPages count from its parent CarouselIndicator.
|
||||||
public var numberOfPages: Int? {
|
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 }
|
guard subviews.isEmpty else { return }
|
||||||
|
|
||||||
|
isUserInteractionEnabled = false
|
||||||
|
|
||||||
addSubview(titleLabel)
|
addSubview(titleLabel)
|
||||||
titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
|
titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
|
||||||
NSLayoutConstraint.constraintPinSubview(titleLabel, pinTop: true, pinBottom: true, pinLeft: false, pinRight: false)
|
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)
|
addSubview(leftArrow)
|
||||||
NSLayoutConstraint.constraintPinView(leftArrow, heightConstraint: true, heightConstant: PaddingTwo, widthConstraint: true, widthConstant: PaddingTwo)
|
NSLayoutConstraint.constraintPinView(leftArrow, heightConstraint: true, heightConstant: PaddingTwo, widthConstraint: true, widthConstant: PaddingTwo)
|
||||||
leftArrow.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
|
leftArrow.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
|
||||||
leftArrow.isUserInteractionEnabled = true
|
|
||||||
|
|
||||||
leftArrow.loadImage(withName: "peakingRightArrow", width: nil, height: nil) { [weak self] image, _, _ in
|
let rightArrow = UIImageView(image: UIImage(named: "peakingRightArrow"))
|
||||||
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")
|
|
||||||
addSubview(rightArrow)
|
addSubview(rightArrow)
|
||||||
|
|
||||||
NSLayoutConstraint.constraintPinView(rightArrow, heightConstraint: true, heightConstant: PaddingTwo, widthConstraint: true, widthConstant: PaddingTwo)
|
NSLayoutConstraint.constraintPinView(rightArrow, heightConstraint: true, heightConstant: PaddingTwo, widthConstraint: true, widthConstant: PaddingTwo)
|
||||||
rightArrow.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
|
rightArrow.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
|
||||||
rightArrow.isUserInteractionEnabled = 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-|",
|
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[leftArrowView]-(padding)-[titleLabel]-(padding)-[rightArrowView]-0-|",
|
||||||
options: .directionLeadingToTrailing,
|
options: .directionLeadingToTrailing,
|
||||||
metrics: ["padding": PaddingOne],
|
metrics: ["padding": PaddingOne],
|
||||||
@ -115,36 +105,17 @@ open class NumericIndicatorView: View, IndicatorViewProtocol {
|
|||||||
"rightArrowView": rightArrow]))
|
"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
|
// 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
|
DispatchQueue.main.async { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
self.titleLabel.text = "\(newIndex)/\(self.numberOfPages ?? 0)"
|
self.titleLabel.text = "\(newIndex)/\(totalCount)"
|
||||||
self.layoutIfNeeded()
|
self.layoutIfNeeded()
|
||||||
(self.superview as? CarouselIndicator)?.sendActions(for: .valueChanged)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public protocol CarouselPagingModelProtocol: MoleculeModelProtocol {
|
public protocol CarouselPagingModelProtocol: MoleculeModelProtocol {
|
||||||
var position: Float? {get}
|
var position: Float? { get }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,12 +10,20 @@ import Foundation
|
|||||||
|
|
||||||
|
|
||||||
@objcMembers public class CarouselItemModel: MoleculeContainerModel, CarouselItemModelProtocol {
|
@objcMembers public class CarouselItemModel: MoleculeContainerModel, CarouselItemModelProtocol {
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
public static var identifier: String = "carouselItem"
|
public static var identifier: String = "carouselItem"
|
||||||
public var backgroundColor: Color?
|
public var backgroundColor: Color?
|
||||||
public var peakingUI: Bool?
|
public var peakingUI: Bool?
|
||||||
public var peakingArrowColor: Color?
|
public var peakingArrowColor: Color?
|
||||||
public var moleculeName: String?
|
public var moleculeName: String?
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Keys
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case moleculeName
|
case moleculeName
|
||||||
case backgroundColor
|
case backgroundColor
|
||||||
@ -23,6 +31,10 @@ import Foundation
|
|||||||
case peakingArrowColor
|
case peakingArrowColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Codec
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
required public init(from decoder: Decoder) throws {
|
required public init(from decoder: Decoder) throws {
|
||||||
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
moleculeName = try typeContainer.decodeIfPresent(String.self, forKey: .moleculeName)
|
moleculeName = try typeContainer.decodeIfPresent(String.self, forKey: .moleculeName)
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
open class Carousel: View {
|
open class Carousel: View {
|
||||||
|
|
||||||
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
|
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
|
||||||
|
|
||||||
/// The current index of the collection view. Includes dummy cells when looping.
|
/// 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.
|
/// The index of the page, does not include dummy cells.
|
||||||
var pageIndex: Int {
|
var pageIndex: Int {
|
||||||
get {
|
get { return loop ? currentIndex - 2 : currentIndex }
|
||||||
return loop ? currentIndex - 2 : currentIndex
|
set (newIndex) {
|
||||||
}
|
|
||||||
set(newIndex) {
|
|
||||||
currentIndex = loop ? newIndex + 2 : newIndex
|
currentIndex = loop ? newIndex + 2 : newIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -30,7 +28,7 @@ open class Carousel: View {
|
|||||||
|
|
||||||
/// The json for the molecules.
|
/// The json for the molecules.
|
||||||
var molecules: [MoleculeModelProtocol]?
|
var molecules: [MoleculeModelProtocol]?
|
||||||
|
|
||||||
/// The horizontal alignment of the cell in the collection view. Only noticeable if the itemWidthPercent is less than 100%.
|
/// The horizontal alignment of the cell in the collection view. Only noticeable if the itemWidthPercent is less than 100%.
|
||||||
var itemAlignment = UICollectionView.ScrollPosition.left
|
var itemAlignment = UICollectionView.ScrollPosition.left
|
||||||
|
|
||||||
@ -53,9 +51,8 @@ open class Carousel: View {
|
|||||||
// MARK: - MVMCoreViewProtocol
|
// MARK: - MVMCoreViewProtocol
|
||||||
open override func setupView() {
|
open override func setupView() {
|
||||||
super.setupView()
|
super.setupView()
|
||||||
guard collectionView.superview == nil else {
|
guard collectionView.superview == nil else { return }
|
||||||
return
|
|
||||||
}
|
|
||||||
collectionView.translatesAutoresizingMaskIntoConstraints = false
|
collectionView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
collectionView.dataSource = self
|
collectionView.dataSource = self
|
||||||
collectionView.delegate = self
|
collectionView.delegate = self
|
||||||
@ -73,7 +70,7 @@ open class Carousel: View {
|
|||||||
super.updateView(size)
|
super.updateView(size)
|
||||||
collectionView.collectionViewLayout.invalidateLayout()
|
collectionView.collectionViewLayout.invalidateLayout()
|
||||||
showPeaking(false)
|
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.
|
// 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 {
|
DispatchQueue.main.async {
|
||||||
self.collectionView.scrollToItem(at: IndexPath(row: self.currentIndex, section: 0), at: self.itemAlignment, animated: false)
|
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
|
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||||
|
|
||||||
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
public override func setWithModel(_ model: MoleculeModelProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||||
super.setWithModel(model, delegateObject, additionalData)
|
super.setWithModel(model, delegateObject, additionalData)
|
||||||
|
|
||||||
guard let carouselModel = model as? CarouselModel else { return }
|
guard let carouselModel = model as? CarouselModel else { return }
|
||||||
|
|
||||||
collectionView.backgroundColor = backgroundColor
|
collectionView.backgroundColor = backgroundColor
|
||||||
collectionView.layer.borderColor = backgroundColor?.cgColor
|
collectionView.layer.borderColor = backgroundColor?.cgColor
|
||||||
collectionView.layer.borderWidth = (carouselModel.border ?? false) ? 1 : 0
|
collectionView.layer.borderWidth = (carouselModel.border ?? false) ? 1 : 0
|
||||||
backgroundColor = .white
|
backgroundColor = .white
|
||||||
|
|
||||||
registerCells(with: carouselModel, delegateObject: delegateObject)
|
registerCells(with: carouselModel, delegateObject: delegateObject)
|
||||||
setupLayout(with: carouselModel)
|
setupLayout(with: carouselModel)
|
||||||
prepareMolecules(with: carouselModel)
|
prepareMolecules(with: carouselModel)
|
||||||
itemWidthPercent = (carouselModel.itemWidthPercent ?? 100) / 100
|
itemWidthPercent = (carouselModel.itemWidthPercent ?? 100) / 100
|
||||||
setAlignment(with: carouselModel.itemAlignment)
|
setAlignment(with: carouselModel.itemAlignment)
|
||||||
|
|
||||||
if let height = carouselModel.height {
|
if let height = carouselModel.height {
|
||||||
collectionViewHeight?.constant = CGFloat(height)
|
collectionViewHeight?.constant = CGFloat(height)
|
||||||
collectionViewHeight?.isActive = true
|
collectionViewHeight?.isActive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
setupPagingMolecule(carouselModel.pagingMolecule, delegateObject: delegateObject)
|
setupPagingMolecule(carouselModel.pagingMolecule, delegateObject: delegateObject)
|
||||||
collectionView.reloadData()
|
collectionView.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - JSON Setters
|
// MARK: - JSON Setters
|
||||||
/// Updates the layout being used
|
/// Updates the layout being used
|
||||||
|
|
||||||
func setupLayout(with carouselModel: CarouselModel?) {
|
func setupLayout(with carouselModel: CarouselModel?) {
|
||||||
let layout = UICollectionViewFlowLayout()
|
let layout = UICollectionViewFlowLayout()
|
||||||
layout.scrollDirection = .horizontal
|
layout.scrollDirection = .horizontal
|
||||||
@ -116,14 +116,14 @@ open class Carousel: View {
|
|||||||
layout.minimumInteritemSpacing = 0
|
layout.minimumInteritemSpacing = 0
|
||||||
collectionView.collectionViewLayout = layout
|
collectionView.collectionViewLayout = layout
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareMolecules(with carouselModel: CarouselModel?) {
|
func prepareMolecules(with carouselModel: CarouselModel?) {
|
||||||
guard let newMolecules = carouselModel?.molecules else {
|
guard let newMolecules = carouselModel?.molecules else {
|
||||||
numberOfPages = 0
|
numberOfPages = 0
|
||||||
molecules = nil
|
molecules = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
numberOfPages = newMolecules.count
|
numberOfPages = newMolecules.count
|
||||||
molecules = newMolecules
|
molecules = newMolecules
|
||||||
if carouselModel?.loop ?? false && newMolecules.count > 2 {
|
if carouselModel?.loop ?? false && newMolecules.count > 2 {
|
||||||
@ -136,16 +136,17 @@ open class Carousel: View {
|
|||||||
}
|
}
|
||||||
pageIndex = 0
|
pageIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets up the paging molecule
|
/// Sets up the paging molecule
|
||||||
open func setupPagingMolecule(_ molecule: CarouselPagingModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) {
|
open func setupPagingMolecule(_ molecule: CarouselPagingModelProtocol?, delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
|
|
||||||
var pagingView: (UIView & MVMCoreUIPagingProtocol)? = nil
|
var pagingView: (UIView & MVMCoreUIPagingProtocol)? = nil
|
||||||
if let molecule = molecule {
|
if let molecule = molecule {
|
||||||
pagingView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(molecule, delegateObject, false) as? (UIView & MVMCoreUIPagingProtocol)
|
pagingView = MVMCoreUIMoleculeMappingObject.shared()?.createMolecule(molecule, delegateObject, false) as? (UIView & MVMCoreUIPagingProtocol)
|
||||||
}
|
}
|
||||||
addPaging(view: pagingView, position: (CGFloat(molecule?.position ?? 20)))
|
addPaging(view: pagingView, position: (CGFloat(molecule?.position ?? 20)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers the cells with the collection view
|
/// Registers the cells with the collection view
|
||||||
func registerCells(with carouselModel: CarouselModel, delegateObject: MVMCoreUIDelegateObject?) {
|
func registerCells(with carouselModel: CarouselModel, delegateObject: MVMCoreUIDelegateObject?) {
|
||||||
for molecule in carouselModel.molecules {
|
for molecule in carouselModel.molecules {
|
||||||
@ -154,19 +155,20 @@ open class Carousel: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Convenience
|
// MARK: - Convenience
|
||||||
/// Returns the (identifier, class) of the molecule for the given map.
|
/// Returns the (identifier, class) of the molecule for the given map.
|
||||||
func getMoleculeInfo(with molecule: MoleculeModelProtocol, delegateObject: MVMCoreUIDelegateObject?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? {
|
func getMoleculeInfo(with molecule: MoleculeModelProtocol, delegateObject: MVMCoreUIDelegateObject?) -> (identifier: String, class: AnyClass, molecule: MoleculeModelProtocol)? {
|
||||||
guard let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(molecule) ,
|
guard let className = MVMCoreUIMoleculeMappingObject.shared()?.getMoleculeClass(molecule),
|
||||||
let moleculeName = (className as? ModelMoleculeViewProtocol.Type)?.nameForReuse(molecule, delegateObject) ?? molecule.moleculeName else {
|
let moleculeName = (className as? ModelMoleculeViewProtocol.Type)?.nameForReuse(molecule, delegateObject) ?? molecule.moleculeName
|
||||||
return nil
|
else { return nil }
|
||||||
}
|
|
||||||
return (moleculeName, className, molecule)
|
return (moleculeName, className, molecule)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the alignment from the string.
|
/// Sets the alignment from the string.
|
||||||
open func setAlignment(with string: String?) {
|
open func setAlignment(with string: String?) {
|
||||||
|
|
||||||
switch string {
|
switch string {
|
||||||
case "leading":
|
case "leading":
|
||||||
itemAlignment = .left
|
itemAlignment = .left
|
||||||
@ -213,6 +215,7 @@ open class Carousel: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open func showPeaking(_ peaking: Bool) {
|
open func showPeaking(_ peaking: Bool) {
|
||||||
|
|
||||||
if peaking && !UIAccessibility.isVoiceOverRunning {
|
if peaking && !UIAccessibility.isVoiceOverRunning {
|
||||||
// Show overlay and arrow in peaking Cell
|
// Show overlay and arrow in peaking Cell
|
||||||
let visibleItemsPaths = collectionView.indexPathsForVisibleItems.sorted { $0.row < $1.row }
|
let visibleItemsPaths = collectionView.indexPathsForVisibleItems.sorted { $0.row < $1.row }
|
||||||
@ -231,9 +234,8 @@ open class Carousel: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setAccessiblity(_ cell: UICollectionViewCell?, index: Int) {
|
func setAccessiblity(_ cell: UICollectionViewCell?, index: Int) {
|
||||||
guard let cell = cell else {
|
guard let cell = cell else { return }
|
||||||
return
|
|
||||||
}
|
|
||||||
if index == currentIndex {
|
if index == currentIndex {
|
||||||
cell.accessibilityElementsHidden = false
|
cell.accessibilityElementsHidden = false
|
||||||
var array = cell.accessibilityElements
|
var array = cell.accessibilityElements
|
||||||
@ -269,9 +271,9 @@ extension Carousel: UICollectionViewDataSource {
|
|||||||
|
|
||||||
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
guard let molecule = molecules?[indexPath.row],
|
guard let molecule = molecules?[indexPath.row],
|
||||||
let moleculeInfo = getMoleculeInfo(with: molecule, delegateObject: nil) else {
|
let moleculeInfo = getMoleculeInfo(with: molecule, delegateObject: nil)
|
||||||
return UICollectionViewCell()
|
else { return UICollectionViewCell() }
|
||||||
}
|
|
||||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: moleculeInfo.identifier, for: indexPath)
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: moleculeInfo.identifier, for: indexPath)
|
||||||
if let protocolCell = cell as? MVMCoreUIMoleculeViewProtocol & ModelMoleculeViewProtocol {
|
if let protocolCell = cell as? MVMCoreUIMoleculeViewProtocol & ModelMoleculeViewProtocol {
|
||||||
protocolCell.reset?()
|
protocolCell.reset?()
|
||||||
@ -286,6 +288,7 @@ extension Carousel: UICollectionViewDataSource {
|
|||||||
extension Carousel: UIScrollViewDelegate {
|
extension Carousel: UIScrollViewDelegate {
|
||||||
|
|
||||||
func goTo(_ index: Int, animated: Bool) {
|
func goTo(_ index: Int, animated: Bool) {
|
||||||
|
|
||||||
showPeaking(false)
|
showPeaking(false)
|
||||||
setAccessiblity(collectionView.cellForItem(at: IndexPath(row: self.currentIndex, section: 0)), index: index)
|
setAccessiblity(collectionView.cellForItem(at: IndexPath(row: self.currentIndex, section: 0)), index: index)
|
||||||
self.currentIndex = index
|
self.currentIndex = index
|
||||||
@ -297,17 +300,15 @@ extension Carousel: UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleUserOnBufferCell() {
|
func handleUserOnBufferCell() {
|
||||||
guard loop else {
|
guard loop else { return }
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastPageIndex = numberOfPages + 1
|
let lastPageIndex = numberOfPages + 1
|
||||||
let goToIndex = {(index: Int) in
|
let goToIndex = {(index: Int) in
|
||||||
self.goTo(index, animated: false)
|
self.goTo(index, animated: false)
|
||||||
self.collectionView.layoutIfNeeded()
|
self.collectionView.layoutIfNeeded()
|
||||||
self.pagingView?.setPage(self.pageIndex)
|
self.pagingView?.setPage(self.pageIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentIndex < 2 {
|
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.
|
// 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)
|
goToIndex(lastPageIndex)
|
||||||
@ -318,9 +319,8 @@ extension Carousel: UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkForDraggingOutOfBounds(_ scrollView: UIScrollView) {
|
func checkForDraggingOutOfBounds(_ scrollView: UIScrollView) {
|
||||||
guard loop, dragging else {
|
guard loop, dragging else { return }
|
||||||
return
|
|
||||||
}
|
|
||||||
// Checks if the user is not paging but attempting to drag endlessly and goes out of bounds. Caps the index.
|
// 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 {
|
if let separatorWidth = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing {
|
||||||
let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent)
|
let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent)
|
||||||
@ -346,24 +346,25 @@ extension Carousel: UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||||
|
|
||||||
dragging = true
|
dragging = true
|
||||||
showPeaking(false)
|
showPeaking(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
||||||
|
|
||||||
dragging = false
|
dragging = false
|
||||||
targetContentOffset.pointee = scrollView.contentOffset
|
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).
|
// 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 {
|
guard let separatorWidth = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.minimumLineSpacing else { return }
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// We switch cards if we pass the velocity threshold or position threshold (currently 50%).
|
// We switch cards if we pass the velocity threshold or position threshold (currently 50%).
|
||||||
let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent)
|
let itemWidth = collectionView.bounds.width * CGFloat(itemWidthPercent)
|
||||||
var cellToSwipeTo = Int(scrollView.contentOffset.x/(itemWidth + separatorWidth) + 0.5)
|
var cellToSwipeTo = Int(scrollView.contentOffset.x/(itemWidth + separatorWidth) + 0.5)
|
||||||
let lastCellIndex = collectionView(collectionView, numberOfItemsInSection: 0) - 1
|
let lastCellIndex = collectionView(collectionView, numberOfItemsInSection: 0) - 1
|
||||||
let velocityThreshold: CGFloat = 1.1
|
let velocityThreshold: CGFloat = 1.1
|
||||||
|
|
||||||
if velocity.x > velocityThreshold {
|
if velocity.x > velocityThreshold {
|
||||||
cellToSwipeTo = currentIndex + 1
|
cellToSwipeTo = currentIndex + 1
|
||||||
} else if velocity.x < -velocityThreshold {
|
} else if velocity.x < -velocityThreshold {
|
||||||
|
|||||||
@ -9,10 +9,14 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@objcMembers public class CarouselModel: MoleculeModelProtocol {
|
@objcMembers public class CarouselModel: MoleculeModelProtocol {
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
public static var identifier: String = "carousel"
|
public static var identifier: String = "carousel"
|
||||||
public var backgroundColor: Color?
|
public var backgroundColor: Color?
|
||||||
public var molecules: [CarouselItemModel]
|
public var molecules: [CarouselItemModel]
|
||||||
|
public var moleculeName: String?
|
||||||
public var spacing: Float?
|
public var spacing: Float?
|
||||||
public var border: Bool?
|
public var border: Bool?
|
||||||
public var loop: Bool?
|
public var loop: Bool?
|
||||||
@ -20,12 +24,20 @@ import UIKit
|
|||||||
public var itemWidthPercent: Float?
|
public var itemWidthPercent: Float?
|
||||||
public var itemAlignment: String?
|
public var itemAlignment: String?
|
||||||
public var pagingMolecule: CarouselPagingModelProtocol?
|
public var pagingMolecule: CarouselPagingModelProtocol?
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Initializer
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
public init(molecules: [CarouselItemModel]){
|
public init(molecules: [CarouselItemModel]){
|
||||||
self.molecules = molecules
|
self.molecules = molecules
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
//--------------------------------------------------
|
||||||
|
// MARK: - Keys
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
case moleculeName
|
case moleculeName
|
||||||
case backgroundColor
|
case backgroundColor
|
||||||
case molecules
|
case molecules
|
||||||
@ -36,32 +48,36 @@ import UIKit
|
|||||||
case itemWidthPercent
|
case itemWidthPercent
|
||||||
case itemAlignment
|
case itemAlignment
|
||||||
case pagingMolecule
|
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)
|
let typeContainer = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
self.molecules = try typeContainer.decode([CarouselItemModel].self, forKey: .molecules)
|
molecules = try typeContainer.decode([CarouselItemModel].self, forKey: .molecules)
|
||||||
self.backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor)
|
||||||
self.spacing = try typeContainer.decode(Float.self, forKey: .spacing)
|
spacing = try typeContainer.decodeIfPresent(Float.self, forKey: .spacing)
|
||||||
self.border = try typeContainer.decode(Bool.self, forKey: .border)
|
border = try typeContainer.decodeIfPresent(Bool.self, forKey: .border)
|
||||||
self.loop = try typeContainer.decode(Bool.self, forKey: .loop)
|
loop = try typeContainer.decodeIfPresent(Bool.self, forKey: .loop)
|
||||||
self.height = try typeContainer.decode(Float.self, forKey: .height)
|
height = try typeContainer.decodeIfPresent(Float.self, forKey: .height)
|
||||||
self.itemWidthPercent = try typeContainer.decode(Float.self, forKey: .itemWidthPercent)
|
itemWidthPercent = try typeContainer.decodeIfPresent(Float.self, forKey: .itemWidthPercent)
|
||||||
self.itemAlignment = try typeContainer.decode(String.self, forKey: .itemAlignment)
|
itemAlignment = try typeContainer.decodeIfPresent(String.self, forKey: .itemAlignment)
|
||||||
self.pagingMolecule = try typeContainer.decodeMoleculeIfPresent(codingKey: .pagingMolecule) as? CarouselPagingModelProtocol
|
pagingMolecule = try typeContainer.decodeMoleculeIfPresent(codingKey: .pagingMolecule) as? CarouselPagingModelProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
try container.encodeIfPresent(moleculeName, forKey: .moleculeName)
|
try container.encodeIfPresent(moleculeName, forKey: .moleculeName)
|
||||||
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor)
|
||||||
try container.encode(molecules, forKey: .molecules)
|
try container.encodeIfPresent(molecules, forKey: .molecules)
|
||||||
try container.encode(spacing, forKey: .spacing)
|
try container.encodeIfPresent(spacing, forKey: .spacing)
|
||||||
try container.encode(border, forKey: .border)
|
try container.encodeIfPresent(border, forKey: .border)
|
||||||
try container.encode(loop, forKey: .loop)
|
try container.encodeIfPresent(loop, forKey: .loop)
|
||||||
try container.encode(height, forKey: .height)
|
try container.encodeIfPresent(height, forKey: .height)
|
||||||
try container.encode(itemWidthPercent, forKey: .itemWidthPercent)
|
try container.encodeIfPresent(itemWidthPercent, forKey: .itemWidthPercent)
|
||||||
try container.encode(itemAlignment, forKey: .itemAlignment)
|
try container.encodeIfPresent(itemAlignment, forKey: .itemAlignment)
|
||||||
try container.encodeModelIfPresent(pagingMolecule, forKey: .pagingMolecule)
|
try container.encodeModelIfPresent(pagingMolecule, forKey: .pagingMolecule)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -94,6 +94,8 @@ import Foundation
|
|||||||
// Other Organisms
|
// Other Organisms
|
||||||
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Carousel.self, viewModelClass: CarouselModel.self)
|
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: Carousel.self, viewModelClass: CarouselModel.self)
|
||||||
|
|
||||||
|
MVMCoreUIMoleculeMappingObject.shared()?.register(viewClass: CarouselIndicator.self, viewModelClass: CarouselIndicatorModel.self)
|
||||||
|
|
||||||
// TODO: Need model
|
// TODO: Need model
|
||||||
MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping.setObject(DigitEntryField.self, forKey: "digitTextField" as NSString)
|
MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping.setObject(DigitEntryField.self, forKey: "digitTextField" as NSString)
|
||||||
MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping.setObject(DateDropdownEntryField.self, forKey: "dateDropdownEntryField" as NSString)
|
MVMCoreUIMoleculeMappingObject.shared()?.moleculeMapping.setObject(DateDropdownEntryField.self, forKey: "dateDropdownEntryField" as NSString)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user