diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 9079fc39..76bd5c1f 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -215,6 +215,10 @@ BB2BF0EC2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2BF0EB2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift */; }; BB2C968F24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C968D24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift */; }; BB2C969224330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C969124330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift */; }; + BB2FB3BB247E7EBC00DF73CD /* TagCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2FB3BA247E7EBC00DF73CD /* TagCollectionViewCell.swift */; }; + BB2FB3BD247E7EF200DF73CD /* TagsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2FB3BC247E7EF200DF73CD /* TagsList.swift */; }; + BB2FB3BF247E7F0900DF73CD /* TagsListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2FB3BE247E7F0900DF73CD /* TagsListModel.swift */; }; + BB2FB3C1247EC1EB00DF73CD /* CollectionViewCenterLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2FB3C0247EC1EB00DF73CD /* CollectionViewCenterLayout.swift */; }; BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47A585241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift */; }; BB47A588241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47A587241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift */; }; BB54C5202434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB54C51E2434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift */; }; @@ -641,6 +645,10 @@ BB2BF0EB2452A9D5001D0FC2 /* ListDeviceComplexButtonSmallModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListDeviceComplexButtonSmallModel.swift; sourceTree = ""; }; BB2C968D24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRightVariableTextLinkAllTextAndLinksModel.swift; sourceTree = ""; }; BB2C969124330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRightVariableTextLinkAllTextAndLinks.swift; sourceTree = ""; }; + BB2FB3BA247E7EBC00DF73CD /* TagCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagCollectionViewCell.swift; sourceTree = ""; }; + BB2FB3BC247E7EF200DF73CD /* TagsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsList.swift; sourceTree = ""; }; + BB2FB3BE247E7F0900DF73CD /* TagsListModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsListModel.swift; sourceTree = ""; }; + BB2FB3C0247EC1EB00DF73CD /* CollectionViewCenterLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewCenterLayout.swift; sourceTree = ""; }; BB47A585241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextDividerSubsectionModel.swift; sourceTree = ""; }; BB47A587241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextDividerSubsection.swift; sourceTree = ""; }; BB54C51E2434D92F0038326C /* ListRightVariableButtonAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListRightVariableButtonAllTextAndLinks.swift; sourceTree = ""; }; @@ -1404,6 +1412,10 @@ D264FAA8243FE17A00D98315 /* Selectors */ = { isa = PBXGroup; children = ( + BB2FB3BA247E7EBC00DF73CD /* TagCollectionViewCell.swift */, + BB2FB3C0247EC1EB00DF73CD /* CollectionViewCenterLayout.swift */, + BB2FB3BC247E7EF200DF73CD /* TagsList.swift */, + BB2FB3BE247E7F0900DF73CD /* TagsListModel.swift */, D264FAAB2441009400D98315 /* RadioBoxCollectionViewCell.swift */, BBAA4F01243D8E3B005AAD5F /* RadioBoxesModel.swift */, BBAA4EFF243D8E3B005AAD5F /* RadioBoxes.swift */, @@ -2090,6 +2102,7 @@ D2755D7B23689C7500485468 /* TableViewCell.swift in Sources */, 0A25209624645AFD000FA9F6 /* TextViewEntryField.swift in Sources */, 014AA72623C501E2006F3E93 /* ContainerModelProtocol.swift in Sources */, + BB2FB3BF247E7F0900DF73CD /* TagsListModel.swift in Sources */, AA11A42123F15D7000D7962F /* ListRightVariablePaymentsModel.swift in Sources */, 011D9626240EBB16000E3791 /* RadioButtonLabelModel.swift in Sources */, 8DDD6C1D244D90B8006A2232 /* ListThreeColumnDataUsage.swift in Sources */, @@ -2155,6 +2168,7 @@ D260105D23D0BCD400764D80 /* Stack.swift in Sources */, 0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */, BB54C5212434D92F0038326C /* ListRightVariableButtonAllTextAndLinksModel.swift in Sources */, + BB2FB3C1247EC1EB00DF73CD /* CollectionViewCenterLayout.swift in Sources */, D2092349244A51D40044AD09 /* RadioSwatchModel.swift in Sources */, 8DD1E370243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift in Sources */, D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */, @@ -2224,6 +2238,7 @@ D253BB9E2458751F002DE544 /* BGImageMoleculeModel.swift in Sources */, 0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */, 8D24041123E7FB9E009E23BE /* ListLeftVariableIconWithRightCaret.swift in Sources */, + BB2FB3BD247E7EF200DF73CD /* TagsList.swift in Sources */, BBAA4F03243D8E3B005AAD5F /* RadioBoxes.swift in Sources */, D2E1FAE12268E81D00AEFD8C /* MoleculeListTemplate.swift in Sources */, 525019E72406853600EED91C /* ListFourColumnDataUsageDivider.swift in Sources */, @@ -2267,6 +2282,7 @@ 0A21DB83235DFBC500C160A2 /* MdnEntryField.swift in Sources */, 0AE98BB723FF18E9004C5109 /* ArrowModel.swift in Sources */, D28A837D23CCA86A00DFE4FC /* TabsListItemModel.swift in Sources */, + BB2FB3BB247E7EBC00DF73CD /* TagCollectionViewCell.swift in Sources */, 012A88C6238DA34000FE3DA1 /* ModuleMoleculeModel.swift in Sources */, 94C2D9A123872BCC0006CF46 /* LabelAttributeUnderlineModel.swift in Sources */, D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/CollectionViewCenterLayout.swift b/MVMCoreUI/Atomic/Atoms/Selectors/CollectionViewCenterLayout.swift new file mode 100644 index 00000000..671629b9 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/CollectionViewCenterLayout.swift @@ -0,0 +1,61 @@ +// +// CollectionViewCenterLayout.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 27/05/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + +class CollectionViewRow { + var attributes = [UICollectionViewLayoutAttributes]() + var spacing: CGFloat = 0 + + + + init(spacing: CGFloat) { + self.spacing = spacing + } + + func add(attribute: UICollectionViewLayoutAttributes) { + attributes.append(attribute) + } + + var rowWidth: CGFloat { + return attributes.reduce(0, { result, attribute -> CGFloat in + return result + attribute.frame.width + }) + CGFloat(attributes.count - 1) * spacing + } + + func centerLayout(collectionViewWidth: CGFloat) { + let padding = (collectionViewWidth - rowWidth) / 2 + var offset = padding + for attribute in attributes { + attribute.frame.origin.x = offset + offset += attribute.frame.width + spacing + } + } +} + +class UICollectionViewCenterLayout: UICollectionViewFlowLayout { + override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { + guard let attributes = super.layoutAttributesForElements(in: rect) else { + return nil + } + + var rows = [CollectionViewRow]() + var currentRowY: CGFloat = -1 + + for attribute in attributes { + if currentRowY != attribute.frame.origin.y { + currentRowY = attribute.frame.origin.y + rows.append(CollectionViewRow(spacing: 10)) + } + rows.last?.add(attribute: attribute) + } + + rows.forEach { $0.centerLayout(collectionViewWidth: collectionView?.frame.width ?? 0) } + return rows.flatMap { $0.attributes } + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/TagCollectionViewCell.swift b/MVMCoreUI/Atomic/Atoms/Selectors/TagCollectionViewCell.swift new file mode 100644 index 00000000..086530f0 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/TagCollectionViewCell.swift @@ -0,0 +1,32 @@ +// +// TagCollectionViewCell.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 27/05/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +open class TagCollectionViewCell: CollectionViewCell { + public let tagLabel = Label() + + open override func reset() { + super.reset() + backgroundColor = .clear + } + + open override func setupView() { + super.setupView() + layer.borderColor = UIColor.mvmCoolGray6.cgColor + layer.borderWidth = 1 + + tagLabel.numberOfLines = 0 + addMolecule(tagLabel) + MVMCoreUIUtility.setMarginsFor(contentView, leading: 15, top: 13, trailing: 15, bottom: 13) + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + guard let model = model as? LabelModel else { return } + tagLabel.set(with: model, delegateObject, additionalData) + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/TagsList.swift b/MVMCoreUI/Atomic/Atoms/Selectors/TagsList.swift new file mode 100644 index 00000000..0f582017 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/TagsList.swift @@ -0,0 +1,120 @@ +// +// TagsList.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 27/05/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +open class TagsList: View { + + public var collectionView: CollectionView! + public var collectionViewHeight: NSLayoutConstraint! + private let itemSpacing: CGFloat = 12.0 + private var tagsListModel: TagsListModel? { + return model as? TagsListModel + } + + private var delegateObject: MVMCoreUIDelegateObject? + + /// The models for the molecules. + public var tags: [LabelModel]? + + + open override func layoutSubviews() { + super.layoutSubviews() + // Accounts for any collection size changes + DispatchQueue.main.async { + self.collectionView.collectionViewLayout.invalidateLayout() + } + } + + // MARK: - MVMCoreViewProtocol + open override func setupView() { + super.setupView() + collectionView = createCollectionView() + addSubview(collectionView) + NSLayoutConstraint.constraintPinSubview(toSuperview: collectionView) + collectionViewHeight = collectionView.heightAnchor.constraint(equalToConstant: 200) + collectionViewHeight?.isActive = true + } + + // MARK: - MoleculeViewProtocol + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + self.delegateObject = delegateObject + + guard let tagsListModel = model as? TagsListModel else { return } + tags = tagsListModel.tags + registerCells() + setHeight() + collectionView.reloadData() + } + + @objc override open func updateView(_ size: CGFloat) { + super.updateView(size) + collectionView.updateView(size) + } + + // MARK: - Creation + + /// Creates the layout for the collection. + open func createCollectionViewLayout() -> UICollectionViewLayout { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .vertical + layout.estimatedItemSize = CGSize(width: 140, height: 40) + layout.minimumLineSpacing = 24.0 + layout.minimumInteritemSpacing = itemSpacing + return layout + } + + /// Creates the collection view. + open func createCollectionView() -> CollectionView { + let collection = CollectionView(frame: .zero, collectionViewLayout: createCollectionViewLayout()) + collection.dataSource = self + collection.delegate = self + return collection + } + + /// Registers the cells with the collection view + open func registerCells() { + collectionView.register(TagCollectionViewCell.self, forCellWithReuseIdentifier: "TagCollectionViewCell") + } + + // MARK: - JSON Setters + open func setHeight() { + + } +} + + + +extension TagsList: UICollectionViewDataSource { + open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return tags?.count ?? 0 + } + + open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let molecule = tags?[indexPath.row], + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TagCollectionViewCell", for: indexPath) as? TagCollectionViewCell else { + fatalError() + } + cell.reset() + cell.set(with: molecule, delegateObject, nil) + cell.layoutIfNeeded() + return cell + } +} + +extension TagsList: UICollectionViewDelegate { + + + + open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + + } + + +} + diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/TagsListModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/TagsListModel.swift new file mode 100644 index 00000000..4784c381 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/TagsListModel.swift @@ -0,0 +1,44 @@ +// +// TagsListModel.swift +// MVMCoreUI +// +// Created by Dhamodaram Nandi on 27/05/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +import Foundation +@objcMembers public class TagsListModel: MoleculeModelProtocol { + + public var backgroundColor: Color? + public static var identifier: String = "tagsList" + public var tags: [LabelModel] + public var borderColor: Color? + + private enum CodingKeys: String, CodingKey { + case moleculeName + case borderColor + case backgroundColor + case tags + + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + borderColor = try typeContainer.decodeIfPresent(Color.self, forKey: .borderColor) + tags = try typeContainer.decode([LabelModel].self, forKey: .tags) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + + + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(tags, forKey: .tags) + try container.encodeIfPresent(borderColor, forKey: .borderColor) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + + + } +} diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index 18b3fee7..6384ea3c 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -73,6 +73,8 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: RadioBoxes.self, viewModelClass: RadioBoxesModel.self) MoleculeObjectMapping.shared()?.register(viewClass: Checkbox.self, viewModelClass: CheckboxModel.self) MoleculeObjectMapping.shared()?.register(viewClass: RadioSwatches.self, viewModelClass: RadioSwatchesModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: TagsList.self, viewModelClass: TagsListModel.self) + // Other Atoms MoleculeObjectMapping.shared()?.register(viewClass: ProgressBar.self, viewModelClass: ProgressBarModel.self)