From c180abf30240a76f7fc4cd1a995381b781f4e981 Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Tue, 31 Mar 2020 13:50:34 +0530 Subject: [PATCH 01/12] Radioswatches model initial commit --- MVMCoreUI.xcodeproj/project.pbxproj | 4 +++ .../Atoms/Buttons/RadioSwatchesModel.swift | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchesModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 03335a4d..7bd74df8 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -173,6 +173,7 @@ AA11A42123F15D7000D7962F /* ListRightVariablePaymentsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA11A42023F15D7000D7962F /* ListRightVariablePaymentsModel.swift */; }; AAA74A172410C04600080241 /* HeadersH2NoButtonsBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */; }; AAA74A192410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */; }; + AAC6F167243332E400F295C1 /* RadioSwatchesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */; }; BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47A585241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift */; }; BB47A588241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47A587241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift */; }; BB6C6AC0242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTallModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB6C6ABE242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTallModel.swift */; }; @@ -556,6 +557,7 @@ AA11A42023F15D7000D7962F /* ListRightVariablePaymentsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariablePaymentsModel.swift; sourceTree = ""; }; AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyText.swift; sourceTree = ""; }; AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyTextModel.swift; sourceTree = ""; }; + AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchesModel.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 = ""; }; BB6C6ABE242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTallModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListOneColumnTextWithWhitespaceDividerTallModel.swift; sourceTree = ""; }; @@ -1492,6 +1494,7 @@ 0116A4E4228B19640094F3ED /* RadioButtonSelectionHelper.swift */, 011D95AE2407266E000E3791 /* RadioButtonModel.swift */, 01004F2F22721C3800991ECC /* RadioButton.swift */, + AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */, ); path = Buttons; sourceTree = ""; @@ -1879,6 +1882,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + AAC6F167243332E400F295C1 /* RadioSwatchesModel.swift in Sources */, 5248BFED23F12E350059236A /* ListThreeColumnPlanDataDividerModel.swift in Sources */, 0A5D59C223AD2F5700EFD9E9 /* AppleGuidelinesProtocol.swift in Sources */, 8D070BB0241B56530099AC56 /* ListRightVariableTotalDataModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchesModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchesModel.swift new file mode 100644 index 00000000..734ecbfd --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchesModel.swift @@ -0,0 +1,35 @@ +// +// RadioSwatchesModel.swift +// MVMCoreUI +// +// Created by Lekshmi S on 31/03/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class RadioSwatchesModel: MoleculeModelProtocol { + public var backgroundColor: Color? + public static var identifier: String = "radioSwatches" + public var moleculeName: String = RadioSwatchesModel.identifier + public var swatches: [RadioSwatchItemModel] + + public init(swatches: [RadioSwatchItemModel]) { + self.swatches = swatches + } +} + +@objcMembers public class RadioSwatchItemModel: MoleculeModelProtocol { + public var backgroundColor: Color? + public static var identifier: String = "radioSwatchItem" + public var moleculeName: String = RadioSwatchItemModel.identifier + public var text: String + public var enabled: Bool + public var color: Color + + public init(enabled: Bool, color: Color, text: String) { + self.enabled = enabled + self.color = color + self.text = text + } +} From 63f225398bdb11dde1e6bede8f2a3a91219304b4 Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Tue, 7 Apr 2020 17:59:28 +0530 Subject: [PATCH 02/12] Added Radioswatches and radioswatchitem class files. --- MVMCoreUI.xcodeproj/project.pbxproj | 8 ++ .../Atoms/Buttons/RadioSwatchItem.swift | 106 ++++++++++++++++++ .../Atomic/Atoms/Buttons/RadioSwatches.swift | 91 +++++++++++++++ .../Atoms/Buttons/RadioSwatchesModel.swift | 76 +++++++++++-- MVMCoreUI/Atomic/MoleculeObjectMapping.swift | 3 + 5 files changed, 277 insertions(+), 7 deletions(-) create mode 100644 MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchItem.swift create mode 100644 MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatches.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 7bd74df8..36a87994 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -173,6 +173,8 @@ AA11A42123F15D7000D7962F /* ListRightVariablePaymentsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA11A42023F15D7000D7962F /* ListRightVariablePaymentsModel.swift */; }; AAA74A172410C04600080241 /* HeadersH2NoButtonsBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */; }; AAA74A192410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */; }; + AAB9C10824346F4B00151545 /* RadioSwatches.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB9C10724346F4B00151545 /* RadioSwatches.swift */; }; + AAB9C10A243496DD00151545 /* RadioSwatchItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB9C109243496DD00151545 /* RadioSwatchItem.swift */; }; AAC6F167243332E400F295C1 /* RadioSwatchesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */; }; BB47A586241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47A585241615EF002BB23C /* ListOneColumnFullWidthTextDividerSubsectionModel.swift */; }; BB47A588241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB47A587241615FA002BB23C /* ListOneColumnFullWidthTextDividerSubsection.swift */; }; @@ -557,6 +559,8 @@ AA11A42023F15D7000D7962F /* ListRightVariablePaymentsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariablePaymentsModel.swift; sourceTree = ""; }; AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyText.swift; sourceTree = ""; }; AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyTextModel.swift; sourceTree = ""; }; + AAB9C10724346F4B00151545 /* RadioSwatches.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatches.swift; sourceTree = ""; }; + AAB9C109243496DD00151545 /* RadioSwatchItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchItem.swift; sourceTree = ""; }; AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchesModel.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 = ""; }; @@ -1495,6 +1499,8 @@ 011D95AE2407266E000E3791 /* RadioButtonModel.swift */, 01004F2F22721C3800991ECC /* RadioButton.swift */, AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */, + AAB9C10724346F4B00151545 /* RadioSwatches.swift */, + AAB9C109243496DD00151545 /* RadioSwatchItem.swift */, ); path = Buttons; sourceTree = ""; @@ -1897,6 +1903,7 @@ D29770F221F7C6D600B2F0D0 /* TopLabelsAndBottomButtonsTableViewController.m in Sources */, D29B771022C281F400D6ACE0 /* ModuleMolecule.swift in Sources */, D28A838923CCCFCB00DFE4FC /* LinkModel.swift in Sources */, + AAB9C10824346F4B00151545 /* RadioSwatches.swift in Sources */, 94C2D9A923872E5E0006CF46 /* LabelAttributeImageModel.swift in Sources */, DBC4391922442197001AB423 /* DashLine.swift in Sources */, 0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */, @@ -1938,6 +1945,7 @@ D2E2A98323D8B32D000B42E6 /* EyebrowHeadlineBodyLinkModel.swift in Sources */, 012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */, BB6C6AC1242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTall.swift in Sources */, + AAB9C10A243496DD00151545 /* RadioSwatchItem.swift in Sources */, D29DF2B421E7B76D003B2FB9 /* MFLoadingSpinner.m in Sources */, 011D9602240DA20A000E3791 /* ValidProtocol.swift in Sources */, D260106323D0C05000764D80 /* StackItemModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchItem.swift b/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchItem.swift new file mode 100644 index 00000000..9693fe62 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchItem.swift @@ -0,0 +1,106 @@ +// +// RadioSwatchItem.swift +// MVMCoreUI +// +// Created by Lekshmi S on 01/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + +open class RadioSwatchItem: UICollectionViewCell, MoleculeViewProtocol { + public var bottomText = Label.commonLabelB2(true) + let circleLayer = CAShapeLayer() + let outerCircleLayer = CAShapeLayer() + var cellView = MVMCoreUICommonViewsUtility.commonView() + var fillColor: Color = Color(uiColor: .mvmBlue) + + open override var isSelected: Bool { + didSet { + drawCircle() + isSelected ? drawOuterCircle() : removeOuterCircle() + isSelected ? (bottomText.isHidden = false) : (bottomText.isHidden = true) + } + } + + var isStrikeThrough: Bool = false { + didSet { + if isStrikeThrough { + drawStrikeThrough() + } + } + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + setupView() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setupView() + } + + public func drawCircle() { + circleLayer.path = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)).cgPath + circleLayer.fillColor = fillColor.cgColor + circleLayer.strokeColor = UIColor.mvmCoolGray6.cgColor + cellView.layer.addSublayer(circleLayer) + } + + public func drawStrikeThrough() { + let startPoint = CGPoint(x: 12, y: 30) + let endPoint = CGPoint(x: 42, y: 0) + let bezierPath = UIBezierPath() + bezierPath.move(to: startPoint) + bezierPath.addLine(to: endPoint) + let strikeThroughLayer = CAShapeLayer() + strikeThroughLayer.path = bezierPath.cgPath + strikeThroughLayer.strokeColor = UIColor.mvmBlack.cgColor + strikeThroughLayer.lineWidth = 1 + cellView.layer.addSublayer(strikeThroughLayer) + isUserInteractionEnabled = false + } + + public func drawOuterCircle() { + circleLayer.path = UIBezierPath(ovalIn: CGRect(x: 15, y: 4, width: 24, height: 24)).cgPath + outerCircleLayer.path = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)).cgPath + outerCircleLayer.name = "OuterCircle" + outerCircleLayer.strokeColor = UIColor.mvmCoolGray6.cgColor + outerCircleLayer.fillColor = UIColor.clear.cgColor + cellView.layer.addSublayer(outerCircleLayer) + } + + public func removeOuterCircle() { + circleLayer.path = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)).cgPath + self.cellView.layer.sublayers?.filter({$0.name == "OuterCircle"}).forEach({$0.removeFromSuperlayer()}) + } + + public func setupView() { + guard cellView.superview == nil else { + return + } + isAccessibilityElement = false + contentView.isAccessibilityElement = false + insetsLayoutMarginsFromSafeArea = false + contentView.insetsLayoutMarginsFromSafeArea = false + contentView.preservesSuperviewLayoutMargins = false + contentView.addSubview(cellView) + NSLayoutConstraint.constraintPinSubview(toSuperview: cellView) + cellView.addSubview(bottomText) + bottomText.textAlignment = .center + bottomText.topAnchor.constraint(equalTo: cellView.topAnchor, constant: 38).isActive = true + bottomText.leadingAnchor.constraint(equalTo: cellView.leadingAnchor).isActive = true + bottomText.trailingAnchor.constraint(equalTo: cellView.trailingAnchor).isActive = true + cellView.bottomAnchor.constraint(equalTo: bottomText.bottomAnchor).isActive = true + } + + public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + guard let collectionModel = model as? RadioSwatchItemModel else { return } + fillColor = collectionModel.color + isSelected = collectionModel.selected ?? false + isUserInteractionEnabled = collectionModel.enabled ?? true + isStrikeThrough = collectionModel.strikethrough ?? false + bottomText.text = collectionModel.text + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatches.swift b/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatches.swift new file mode 100644 index 00000000..5c0c24be --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatches.swift @@ -0,0 +1,91 @@ +// +// RadioSwatches.swift +// MVMCoreUI +// +// Created by Lekshmi S on 01/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + +open class RadioSwatches: View { + public let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + var swatches: [MoleculeModelProtocol]? + public var collectionViewHeight: NSLayoutConstraint? + + public let cellSize: Double = 54.0 + public let spacing: Double = 10 + + open override func setupView() { + super.setupView() + guard collectionView.superview == nil else { + return + } + collectionView.translatesAutoresizingMaskIntoConstraints = false + collectionView.dataSource = self + collectionView.delegate = self + collectionView.showsHorizontalScrollIndicator = false + collectionView.backgroundColor = .clear + collectionView.isAccessibilityElement = false + addSubview(collectionView) + NSLayoutConstraint.constraintPinSubview(toSuperview: collectionView) + collectionViewHeight = collectionView.heightAnchor.constraint(equalToConstant: 100) + collectionViewHeight?.isActive = true + } + + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let radioSwatchesModel = model as? RadioSwatchesModel else { return } + collectionView.layer.borderColor = backgroundColor?.cgColor + registerCells() + setupLayout(with: radioSwatchesModel) + prepareMolecules(with: radioSwatchesModel) + collectionView.reloadData() + } + + func registerCells() { + collectionView.register(RadioSwatchItem.self, forCellWithReuseIdentifier: "RadioSwatchItemCollectionViewCell") + } + + func setupLayout(with radioSwatchesModel: RadioSwatchesModel?) { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .vertical + layout.minimumLineSpacing = CGFloat(spacing) + layout.minimumInteritemSpacing = CGFloat(spacing) + collectionView.collectionViewLayout = layout + } + + func prepareMolecules(with radioSwatchesModel: RadioSwatchesModel?) { + guard let newSwatches = radioSwatchesModel?.swatches else { + swatches = nil + return + } + swatches = newSwatches + let collectionViewWidth = UIScreen.main.bounds.width - (2 * MFStyler.defaultHorizontalPaddingForApplicationWidth()) + let swatchesInRow = Double(floor(Double(collectionViewWidth/60.0))) + let numberOfRows = floor(Double(swatches?.count ?? 1)/swatchesInRow) + 1.0 + let height = (numberOfRows * cellSize) + (spacing * (numberOfRows-1)) + collectionViewHeight?.constant = CGFloat(height) + collectionViewHeight?.isActive = true + } +} + +extension RadioSwatches: UICollectionViewDelegateFlowLayout { + open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: cellSize, height: cellSize) + } +} + +extension RadioSwatches: UICollectionViewDataSource { + open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return swatches?.count ?? 0 + } + + open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let molecule = swatches?[indexPath.row], let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RadioSwatchItemCollectionViewCell", for: indexPath) as? RadioSwatchItem else { + return UICollectionViewCell() + } + cell.set(with: molecule, nil, nil) + return cell + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchesModel.swift b/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchesModel.swift index 734ecbfd..ba6cddef 100644 --- a/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchesModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchesModel.swift @@ -11,25 +11,87 @@ import Foundation @objcMembers public class RadioSwatchesModel: MoleculeModelProtocol { public var backgroundColor: Color? public static var identifier: String = "radioSwatches" - public var moleculeName: String = RadioSwatchesModel.identifier public var swatches: [RadioSwatchItemModel] public init(swatches: [RadioSwatchItemModel]) { self.swatches = swatches } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case swatches + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + self.swatches = try typeContainer.decode([RadioSwatchItemModel].self, forKey: .swatches) + self.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.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encode(swatches, forKey: .swatches) + } } @objcMembers public class RadioSwatchItemModel: MoleculeModelProtocol { public var backgroundColor: Color? public static var identifier: String = "radioSwatchItem" - public var moleculeName: String = RadioSwatchItemModel.identifier - public var text: String - public var enabled: Bool - public var color: Color + public var color: Color = Color(uiColor: .mvmBlue) + public var text: String? + public var selected: Bool? = false + public var enabled: Bool? = true + public var strikethrough: Bool? = false - public init(enabled: Bool, color: Color, text: String) { - self.enabled = enabled + public init(color: Color, text:String, selected: Bool, enabled: Bool, strikethrough: Bool) { self.color = color self.text = text + self.selected = selected + self.enabled = enabled + self.strikethrough = strikethrough + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case color + case text + case selected + case enabled + case strikethrough + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + color = try typeContainer.decode(Color.self, forKey: .color) + if let text = try typeContainer.decodeIfPresent(String.self, forKey: .text) { + self.text = text + } + if let selected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected) { + self.selected = selected + } + if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { + self.enabled = enabled + } + if let strikethrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) { + self.strikethrough = strikethrough + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encode(color, forKey: .color) + try container.encode(text, forKey: .text) + try container.encode(selected, forKey: .selected) + try container.encode(enabled, forKey: .enabled) + try container.encode(strikethrough, forKey: .strikethrough) } } + + diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index bfa6db01..67a5cfed 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -111,6 +111,9 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: MoleculeStackItem.self, viewModelClass: MoleculeStackItemModel.self) MoleculeObjectMapping.shared()?.register(viewClass: StackItem.self, viewModelClass: StackItemModel.self) MoleculeObjectMapping.shared()?.register(viewClass: MoleculeCollectionViewCell.self, viewModelClass: CarouselItemModel.self) + + MoleculeObjectMapping.shared()?.register(viewClass: RadioSwatchItem.self, viewModelClass: RadioSwatchItemModel.self) + MoleculeObjectMapping.shared()?.register(viewClass: RadioSwatches.self, viewModelClass: RadioSwatchesModel.self) // Other Container Molecules MoleculeObjectMapping.shared()?.register(viewClass: MoleculeHeaderView.self, viewModelClass: MoleculeHeaderModel.self) From 237ddb82251e2daefdaee2c591d9b427a2b0f6dc Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Tue, 7 Apr 2020 18:07:40 +0530 Subject: [PATCH 03/12] Added folder structure --- MVMCoreUI.xcodeproj/project.pbxproj | 22 +++++++++++++------ .../RadioSwatchItem.swift | 0 .../RadioSwatches.swift | 0 .../RadioSwatchesModel.swift | 0 4 files changed, 15 insertions(+), 7 deletions(-) rename MVMCoreUI/Atomic/Atoms/{Buttons => Selectors}/RadioSwatchItem.swift (100%) rename MVMCoreUI/Atomic/Atoms/{Buttons => Selectors}/RadioSwatches.swift (100%) rename MVMCoreUI/Atomic/Atoms/{Buttons => Selectors}/RadioSwatchesModel.swift (100%) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index fad7e2e3..17ec4e42 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -182,11 +182,11 @@ AA11A42123F15D7000D7962F /* ListRightVariablePaymentsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA11A42023F15D7000D7962F /* ListRightVariablePaymentsModel.swift */; }; AAA74A172410C04600080241 /* HeadersH2NoButtonsBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */; }; AAA74A192410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */; }; - BB2C968F24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C968D24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift */; }; - BB2C969224330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C969124330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift */; }; AAB9C10824346F4B00151545 /* RadioSwatches.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB9C10724346F4B00151545 /* RadioSwatches.swift */; }; AAB9C10A243496DD00151545 /* RadioSwatchItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB9C109243496DD00151545 /* RadioSwatchItem.swift */; }; AAC6F167243332E400F295C1 /* RadioSwatchesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */; }; + BB2C968F24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C968D24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift */; }; + BB2C969224330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C969124330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.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 */; }; @@ -583,11 +583,11 @@ AA11A42023F15D7000D7962F /* ListRightVariablePaymentsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRightVariablePaymentsModel.swift; sourceTree = ""; }; AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyText.swift; sourceTree = ""; }; AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyTextModel.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 = ""; }; AAB9C10724346F4B00151545 /* RadioSwatches.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatches.swift; sourceTree = ""; }; AAB9C109243496DD00151545 /* RadioSwatchItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchItem.swift; sourceTree = ""; }; AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchesModel.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 = ""; }; 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 = ""; }; @@ -1055,6 +1055,16 @@ path = RightVariable; sourceTree = ""; }; + AA56A26D243CAB0800303286 /* Selectors */ = { + isa = PBXGroup; + children = ( + AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */, + AAB9C10724346F4B00151545 /* RadioSwatches.swift */, + AAB9C109243496DD00151545 /* RadioSwatchItem.swift */, + ); + path = Selectors; + sourceTree = ""; + }; D202AFE2242A5F1400E5BEDF /* Extensions */ = { isa = PBXGroup; children = ( @@ -1425,6 +1435,7 @@ D29DF10D21E67A70003B2FB9 /* Atoms */ = { isa = PBXGroup; children = ( + AA56A26D243CAB0800303286 /* Selectors */, D29DF22B21E6A0FA003B2FB9 /* TextFields */, D29DF17D21E69E26003B2FB9 /* Views */, D29DF16821E69E1F003B2FB9 /* Buttons */, @@ -1565,9 +1576,6 @@ 0116A4E4228B19640094F3ED /* RadioButtonSelectionHelper.swift */, 011D95AE2407266E000E3791 /* RadioButtonModel.swift */, 01004F2F22721C3800991ECC /* RadioButton.swift */, - AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */, - AAB9C10724346F4B00151545 /* RadioSwatches.swift */, - AAB9C109243496DD00151545 /* RadioSwatchItem.swift */, ); path = Buttons; sourceTree = ""; diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchItem.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift similarity index 100% rename from MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchItem.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatches.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift similarity index 100% rename from MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatches.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift diff --git a/MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchesModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift similarity index 100% rename from MVMCoreUI/Atomic/Atoms/Buttons/RadioSwatchesModel.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift From d351d6cd7c0a46bad043bcb6e9cdbb4f6049f4f3 Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Tue, 7 Apr 2020 19:02:50 +0530 Subject: [PATCH 04/12] Code changes after review --- MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift index ba6cddef..d926ad5c 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift @@ -87,10 +87,10 @@ import Foundation try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encode(color, forKey: .color) - try container.encode(text, forKey: .text) - try container.encode(selected, forKey: .selected) - try container.encode(enabled, forKey: .enabled) - try container.encode(strikethrough, forKey: .strikethrough) + try container.encodeIfPresent(text, forKey: .text) + try container.encodeIfPresent(selected, forKey: .selected) + try container.encodeIfPresent(enabled, forKey: .enabled) + try container.encodeIfPresent(strikethrough, forKey: .strikethrough) } } From f46b9cb339502b7110bf8b7f093e9dff97480bcc Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Wed, 8 Apr 2020 19:57:27 +0530 Subject: [PATCH 05/12] Radio swatch elements - modify story commit --- .../Atomic/Atoms/Selectors/RadioSwatchItem.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift index 9693fe62..3404f7f7 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift @@ -25,6 +25,7 @@ open class RadioSwatchItem: UICollectionViewCell, MoleculeViewProtocol { var isStrikeThrough: Bool = false { didSet { + cellView.layer.sublayers?.filter({$0.name == "OutOfStock"}).forEach({$0.removeFromSuperlayer()}) if isStrikeThrough { drawStrikeThrough() } @@ -44,7 +45,8 @@ open class RadioSwatchItem: UICollectionViewCell, MoleculeViewProtocol { public func drawCircle() { circleLayer.path = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)).cgPath circleLayer.fillColor = fillColor.cgColor - circleLayer.strokeColor = UIColor.mvmCoolGray6.cgColor + circleLayer.strokeColor = UIColor.mvmBlack.cgColor + circleLayer.lineWidth = 1 cellView.layer.addSublayer(circleLayer) } @@ -56,18 +58,19 @@ open class RadioSwatchItem: UICollectionViewCell, MoleculeViewProtocol { bezierPath.addLine(to: endPoint) let strikeThroughLayer = CAShapeLayer() strikeThroughLayer.path = bezierPath.cgPath - strikeThroughLayer.strokeColor = UIColor.mvmBlack.cgColor + strikeThroughLayer.name = "OutOfStock" + strikeThroughLayer.strokeColor = UIColor.mvmCoolGray6.cgColor strikeThroughLayer.lineWidth = 1 cellView.layer.addSublayer(strikeThroughLayer) - isUserInteractionEnabled = false } public func drawOuterCircle() { circleLayer.path = UIBezierPath(ovalIn: CGRect(x: 15, y: 4, width: 24, height: 24)).cgPath outerCircleLayer.path = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)).cgPath outerCircleLayer.name = "OuterCircle" - outerCircleLayer.strokeColor = UIColor.mvmCoolGray6.cgColor + outerCircleLayer.strokeColor = UIColor.mvmBlack.cgColor outerCircleLayer.fillColor = UIColor.clear.cgColor + outerCircleLayer.lineWidth = 1 cellView.layer.addSublayer(outerCircleLayer) } From 45901f31101876a9f456866a372fe43244b02161 Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Thu, 9 Apr 2020 19:37:32 +0530 Subject: [PATCH 06/12] Added new keys and code changes. --- .../Atoms/Selectors/RadioSwatchItem.swift | 28 +++++++++-- .../Atoms/Selectors/RadioSwatches.swift | 48 ++++++++++++++++++- .../Atoms/Selectors/RadioSwatchesModel.swift | 28 ++++++++++- 3 files changed, 95 insertions(+), 9 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift index 3404f7f7..17352ed7 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift @@ -9,12 +9,17 @@ import UIKit open class RadioSwatchItem: UICollectionViewCell, MoleculeViewProtocol { - public var bottomText = Label.commonLabelB2(true) + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public var bottomText = Label(frame: .zero) let circleLayer = CAShapeLayer() - let outerCircleLayer = CAShapeLayer() var cellView = MVMCoreUICommonViewsUtility.commonView() var fillColor: Color = Color(uiColor: .mvmBlue) - + + //------------------------------------------------------ + // MARK: - Property Observer + //------------------------------------------------------ open override var isSelected: Bool { didSet { drawCircle() @@ -32,6 +37,9 @@ open class RadioSwatchItem: UICollectionViewCell, MoleculeViewProtocol { } } + //------------------------------------------------------ + // MARK: - Initialization + //------------------------------------------------------ public override init(frame: CGRect) { super.init(frame: .zero) setupView() @@ -42,10 +50,15 @@ open class RadioSwatchItem: UICollectionViewCell, MoleculeViewProtocol { setupView() } + //------------------------------------------------------ + // MARK: - Drawing + //------------------------------------------------------ public func drawCircle() { + cellView.layer.sublayers?.filter({$0.name == "InnerCircle"}).forEach({$0.removeFromSuperlayer()}) circleLayer.path = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)).cgPath circleLayer.fillColor = fillColor.cgColor - circleLayer.strokeColor = UIColor.mvmBlack.cgColor + circleLayer.strokeColor = isUserInteractionEnabled ? UIColor.mvmBlack.cgColor : UIColor.mvmCoolGray6.cgColor + circleLayer.name = "InnerCircle" circleLayer.lineWidth = 1 cellView.layer.addSublayer(circleLayer) } @@ -65,7 +78,9 @@ open class RadioSwatchItem: UICollectionViewCell, MoleculeViewProtocol { } public func drawOuterCircle() { + self.cellView.layer.sublayers?.filter({$0.name == "OuterCircle"}).forEach({$0.removeFromSuperlayer()}) circleLayer.path = UIBezierPath(ovalIn: CGRect(x: 15, y: 4, width: 24, height: 24)).cgPath + let outerCircleLayer = CAShapeLayer() outerCircleLayer.path = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)).cgPath outerCircleLayer.name = "OuterCircle" outerCircleLayer.strokeColor = UIColor.mvmBlack.cgColor @@ -79,6 +94,9 @@ open class RadioSwatchItem: UICollectionViewCell, MoleculeViewProtocol { self.cellView.layer.sublayers?.filter({$0.name == "OuterCircle"}).forEach({$0.removeFromSuperlayer()}) } + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- public func setupView() { guard cellView.superview == nil else { return @@ -92,6 +110,7 @@ open class RadioSwatchItem: UICollectionViewCell, MoleculeViewProtocol { NSLayoutConstraint.constraintPinSubview(toSuperview: cellView) cellView.addSubview(bottomText) bottomText.textAlignment = .center + bottomText.font = MFFonts.mfFontTXRegular(11.0) bottomText.topAnchor.constraint(equalTo: cellView.topAnchor, constant: 38).isActive = true bottomText.leadingAnchor.constraint(equalTo: cellView.leadingAnchor).isActive = true bottomText.trailingAnchor.constraint(equalTo: cellView.trailingAnchor).isActive = true @@ -101,7 +120,6 @@ open class RadioSwatchItem: UICollectionViewCell, MoleculeViewProtocol { public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { guard let collectionModel = model as? RadioSwatchItemModel else { return } fillColor = collectionModel.color - isSelected = collectionModel.selected ?? false isUserInteractionEnabled = collectionModel.enabled ?? true isStrikeThrough = collectionModel.strikethrough ?? false bottomText.text = collectionModel.text diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift index 5c0c24be..a27af6c8 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift @@ -9,13 +9,29 @@ import UIKit open class RadioSwatches: View { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- public let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) - var swatches: [MoleculeModelProtocol]? - public var collectionViewHeight: NSLayoutConstraint? + var swatches: [RadioSwatchItemModel]? + public var selectedSwatchItem: RadioSwatchItemModel? { + get{ + guard let selectedItem = collectionView.indexPathsForSelectedItems?.first else {return nil} + return swatches?[selectedItem.item] + } + } + + //------------------------------------------------------ + // MARK: - Constraints + //------------------------------------------------------ + public var collectionViewHeight: NSLayoutConstraint? public let cellSize: Double = 54.0 public let spacing: Double = 10 + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- open override func setupView() { super.setupView() guard collectionView.superview == nil else { @@ -33,6 +49,17 @@ open class RadioSwatches: View { collectionViewHeight?.isActive = true } + public override func updateView(_ size: CGFloat) { + DispatchQueue.main.async { + self.collectionView.collectionViewLayout.invalidateLayout() + self.collectionView.reloadData() + guard let selectedCell = self.swatches?.firstIndex(where: {$0.selected == true}) else { + return + } + self.collectionView.selectItem(at: IndexPath(item: selectedCell, section: 0), animated: true, scrollPosition: .centeredHorizontally) + } + } + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let radioSwatchesModel = model as? RadioSwatchesModel else { return } @@ -43,6 +70,9 @@ open class RadioSwatches: View { collectionView.reloadData() } + //------------------------------------------------------ + // MARK: - Methods + //------------------------------------------------------ func registerCells() { collectionView.register(RadioSwatchItem.self, forCellWithReuseIdentifier: "RadioSwatchItemCollectionViewCell") } @@ -70,6 +100,9 @@ open class RadioSwatches: View { } } +//------------------------------------------------------ +// MARK: - Delegate methods +//------------------------------------------------------ extension RadioSwatches: UICollectionViewDelegateFlowLayout { open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: cellSize, height: cellSize) @@ -89,3 +122,14 @@ extension RadioSwatches: UICollectionViewDataSource { return cell } } + +extension RadioSwatches: UICollectionViewDelegate { + public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + guard let swatchItem = swatches?[indexPath.row] else { return } + swatchItem.selected = true + } + public func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { + guard let swatchItem = swatches?[indexPath.row] else { return } + swatchItem.selected = false + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift index d926ad5c..e5e0ce11 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift @@ -37,7 +37,7 @@ import Foundation } } -@objcMembers public class RadioSwatchItemModel: MoleculeModelProtocol { +@objcMembers public class RadioSwatchItemModel: MoleculeModelProtocol, FormFieldProtocol { public var backgroundColor: Color? public static var identifier: String = "radioSwatchItem" public var color: Color = Color(uiColor: .mvmBlue) @@ -45,13 +45,25 @@ import Foundation public var selected: Bool? = false public var enabled: Bool? = true public var strikethrough: Bool? = false + public var fieldKey: String? + public var fieldValue: String? + public var baseValue: AnyHashable? + public var groupName: String = FormValidator.defaultGroupName - public init(color: Color, text:String, selected: Bool, enabled: Bool, strikethrough: Bool) { + public init(color: Color, text:String, selected: Bool, enabled: Bool, strikethrough: Bool, fieldKey: String, fieldValue: String, groupName:String) { self.color = color self.text = text self.selected = selected + self.fieldValue = fieldValue self.enabled = enabled self.strikethrough = strikethrough + self.fieldKey = fieldKey + self.groupName = groupName + baseValue = selected + } + + public func formFieldValue() -> AnyHashable? { + return selected } private enum CodingKeys: String, CodingKey { @@ -62,6 +74,9 @@ import Foundation case selected case enabled case strikethrough + case fieldKey + case fieldValue + case groupName } required public init(from decoder: Decoder) throws { @@ -80,6 +95,12 @@ import Foundation if let strikethrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) { self.strikethrough = strikethrough } + baseValue = self.selected + fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) + fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) + if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { + self.groupName = groupName + } } public func encode(to encoder: Encoder) throws { @@ -91,6 +112,9 @@ import Foundation try container.encodeIfPresent(selected, forKey: .selected) try container.encodeIfPresent(enabled, forKey: .enabled) try container.encodeIfPresent(strikethrough, forKey: .strikethrough) + try container.encodeIfPresent(fieldKey, forKey: .fieldKey) + try container.encodeIfPresent(fieldValue, forKey: .fieldValue) + try container.encodeIfPresent(groupName, forKey: .groupName) } } From 5a30f99d62f260f83237d2b5e9d48648e82c04e8 Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Mon, 13 Apr 2020 10:22:30 +0530 Subject: [PATCH 07/12] Re-arranged folder structure. --- MVMCoreUI.xcodeproj/project.pbxproj | 14 +++----------- .../Atoms/{Selectors => }/RadioSwatchItem.swift | 0 .../Atoms/{Selectors => }/RadioSwatches.swift | 0 .../Atoms/{Selectors => }/RadioSwatchesModel.swift | 0 4 files changed, 3 insertions(+), 11 deletions(-) rename MVMCoreUI/Atomic/Atoms/{Selectors => }/RadioSwatchItem.swift (100%) rename MVMCoreUI/Atomic/Atoms/{Selectors => }/RadioSwatches.swift (100%) rename MVMCoreUI/Atomic/Atoms/{Selectors => }/RadioSwatchesModel.swift (100%) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 5bcd1a74..08fbd249 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -1069,16 +1069,6 @@ path = TwoColumn; sourceTree = ""; }; - AA56A26D243CAB0800303286 /* Selectors */ = { - isa = PBXGroup; - children = ( - AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */, - AAB9C10724346F4B00151545 /* RadioSwatches.swift */, - AAB9C109243496DD00151545 /* RadioSwatchItem.swift */, - ); - path = Selectors; - sourceTree = ""; - }; D202AFE2242A5F1400E5BEDF /* Extensions */ = { isa = PBXGroup; children = ( @@ -1453,7 +1443,9 @@ D29DF10D21E67A70003B2FB9 /* Atoms */ = { isa = PBXGroup; children = ( - AA56A26D243CAB0800303286 /* Selectors */, + AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */, + AAB9C10724346F4B00151545 /* RadioSwatches.swift */, + AAB9C109243496DD00151545 /* RadioSwatchItem.swift */, D29DF22B21E6A0FA003B2FB9 /* TextFields */, D29DF17D21E69E26003B2FB9 /* Views */, D29DF16821E69E1F003B2FB9 /* Buttons */, diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift b/MVMCoreUI/Atomic/Atoms/RadioSwatchItem.swift similarity index 100% rename from MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift rename to MVMCoreUI/Atomic/Atoms/RadioSwatchItem.swift diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift b/MVMCoreUI/Atomic/Atoms/RadioSwatches.swift similarity index 100% rename from MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift rename to MVMCoreUI/Atomic/Atoms/RadioSwatches.swift diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift b/MVMCoreUI/Atomic/Atoms/RadioSwatchesModel.swift similarity index 100% rename from MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift rename to MVMCoreUI/Atomic/Atoms/RadioSwatchesModel.swift From 163535398925b20ec54f2ccf7306ef64f9d1bf1e Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Mon, 13 Apr 2020 18:07:54 +0530 Subject: [PATCH 08/12] Code refactoring by using collection base classes. --- MVMCoreUI.xcodeproj/project.pbxproj | 10 +- MVMCoreUI/Atomic/Atoms/RadioSwatchItem.swift | 127 ------------- .../Atoms/Selectors/RadioSwatchItem.swift | 167 ++++++++++++++++++ .../RadioSwatchItemCollectionViewCell.swift | 24 +++ .../Atoms/{ => Selectors}/RadioSwatches.swift | 113 ++++++------ .../{ => Selectors}/RadioSwatchesModel.swift | 53 +++--- MVMCoreUI/Atomic/MoleculeObjectMapping.swift | 1 + 7 files changed, 280 insertions(+), 215 deletions(-) delete mode 100644 MVMCoreUI/Atomic/Atoms/RadioSwatchItem.swift create mode 100644 MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift create mode 100644 MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift rename MVMCoreUI/Atomic/Atoms/{ => Selectors}/RadioSwatches.swift (54%) rename MVMCoreUI/Atomic/Atoms/{ => Selectors}/RadioSwatchesModel.swift (86%) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 3e55a236..077b0ca2 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -190,6 +190,7 @@ AA1EC59924373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1EC59824373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift */; }; AA56A20F243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA56A20E243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift */; }; AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA56A210243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift */; }; + AA85236C244435A20059CC1E /* RadioSwatchItemCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA85236B244435A20059CC1E /* RadioSwatchItemCollectionViewCell.swift */; }; AAA74A172410C04600080241 /* HeadersH2NoButtonsBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */; }; AAA74A192410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */; }; AAB9C10824346F4B00151545 /* RadioSwatches.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB9C10724346F4B00151545 /* RadioSwatches.swift */; }; @@ -619,6 +620,7 @@ AA1EC59824373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnSpeedTestDivider.swift; sourceTree = ""; }; AA56A20E243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnSubsectionDividerModel.swift; sourceTree = ""; }; AA56A210243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnSubsectionDivider.swift; sourceTree = ""; }; + AA85236B244435A20059CC1E /* RadioSwatchItemCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchItemCollectionViewCell.swift; sourceTree = ""; }; AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyText.swift; sourceTree = ""; }; AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyTextModel.swift; sourceTree = ""; }; AAB9C10724346F4B00151545 /* RadioSwatches.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatches.swift; sourceTree = ""; }; @@ -1435,6 +1437,10 @@ 01004F2F22721C3800991ECC /* RadioButton.swift */, 31BE15CA23D8924C00452370 /* CheckboxModel.swift */, 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */, + AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */, + AAB9C10724346F4B00151545 /* RadioSwatches.swift */, + AAB9C109243496DD00151545 /* RadioSwatchItem.swift */, + AA85236B244435A20059CC1E /* RadioSwatchItemCollectionViewCell.swift */, ); path = Selectors; sourceTree = ""; @@ -1515,9 +1521,6 @@ isa = PBXGroup; children = ( D264FAA8243FE17A00D98315 /* Selectors */, - AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */, - AAB9C10724346F4B00151545 /* RadioSwatches.swift */, - AAB9C109243496DD00151545 /* RadioSwatchItem.swift */, D29DF22B21E6A0FA003B2FB9 /* TextFields */, D29DF17D21E69E26003B2FB9 /* Views */, D29DF16821E69E1F003B2FB9 /* Buttons */, @@ -2330,6 +2333,7 @@ 0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */, AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */, D264FA8C243BCD8E00D98315 /* CollectionTemplateModel.swift in Sources */, + AA85236C244435A20059CC1E /* RadioSwatchItemCollectionViewCell.swift in Sources */, 52B201D224081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift in Sources */, D26C5A6B23F4A40D007AEECE /* ListItemModel.swift in Sources */, 0A21DB8D235E06EF00C160A2 /* MFDigitTextField.m in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/RadioSwatchItem.swift b/MVMCoreUI/Atomic/Atoms/RadioSwatchItem.swift deleted file mode 100644 index 17352ed7..00000000 --- a/MVMCoreUI/Atomic/Atoms/RadioSwatchItem.swift +++ /dev/null @@ -1,127 +0,0 @@ -// -// RadioSwatchItem.swift -// MVMCoreUI -// -// Created by Lekshmi S on 01/04/20. -// Copyright © 2020 Verizon Wireless. All rights reserved. -// - -import UIKit - -open class RadioSwatchItem: UICollectionViewCell, MoleculeViewProtocol { - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - public var bottomText = Label(frame: .zero) - let circleLayer = CAShapeLayer() - var cellView = MVMCoreUICommonViewsUtility.commonView() - var fillColor: Color = Color(uiColor: .mvmBlue) - - //------------------------------------------------------ - // MARK: - Property Observer - //------------------------------------------------------ - open override var isSelected: Bool { - didSet { - drawCircle() - isSelected ? drawOuterCircle() : removeOuterCircle() - isSelected ? (bottomText.isHidden = false) : (bottomText.isHidden = true) - } - } - - var isStrikeThrough: Bool = false { - didSet { - cellView.layer.sublayers?.filter({$0.name == "OutOfStock"}).forEach({$0.removeFromSuperlayer()}) - if isStrikeThrough { - drawStrikeThrough() - } - } - } - - //------------------------------------------------------ - // MARK: - Initialization - //------------------------------------------------------ - public override init(frame: CGRect) { - super.init(frame: .zero) - setupView() - } - - public required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - setupView() - } - - //------------------------------------------------------ - // MARK: - Drawing - //------------------------------------------------------ - public func drawCircle() { - cellView.layer.sublayers?.filter({$0.name == "InnerCircle"}).forEach({$0.removeFromSuperlayer()}) - circleLayer.path = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)).cgPath - circleLayer.fillColor = fillColor.cgColor - circleLayer.strokeColor = isUserInteractionEnabled ? UIColor.mvmBlack.cgColor : UIColor.mvmCoolGray6.cgColor - circleLayer.name = "InnerCircle" - circleLayer.lineWidth = 1 - cellView.layer.addSublayer(circleLayer) - } - - public func drawStrikeThrough() { - let startPoint = CGPoint(x: 12, y: 30) - let endPoint = CGPoint(x: 42, y: 0) - let bezierPath = UIBezierPath() - bezierPath.move(to: startPoint) - bezierPath.addLine(to: endPoint) - let strikeThroughLayer = CAShapeLayer() - strikeThroughLayer.path = bezierPath.cgPath - strikeThroughLayer.name = "OutOfStock" - strikeThroughLayer.strokeColor = UIColor.mvmCoolGray6.cgColor - strikeThroughLayer.lineWidth = 1 - cellView.layer.addSublayer(strikeThroughLayer) - } - - public func drawOuterCircle() { - self.cellView.layer.sublayers?.filter({$0.name == "OuterCircle"}).forEach({$0.removeFromSuperlayer()}) - circleLayer.path = UIBezierPath(ovalIn: CGRect(x: 15, y: 4, width: 24, height: 24)).cgPath - let outerCircleLayer = CAShapeLayer() - outerCircleLayer.path = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)).cgPath - outerCircleLayer.name = "OuterCircle" - outerCircleLayer.strokeColor = UIColor.mvmBlack.cgColor - outerCircleLayer.fillColor = UIColor.clear.cgColor - outerCircleLayer.lineWidth = 1 - cellView.layer.addSublayer(outerCircleLayer) - } - - public func removeOuterCircle() { - circleLayer.path = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)).cgPath - self.cellView.layer.sublayers?.filter({$0.name == "OuterCircle"}).forEach({$0.removeFromSuperlayer()}) - } - - //-------------------------------------------------- - // MARK: - Lifecycle - //-------------------------------------------------- - public func setupView() { - guard cellView.superview == nil else { - return - } - isAccessibilityElement = false - contentView.isAccessibilityElement = false - insetsLayoutMarginsFromSafeArea = false - contentView.insetsLayoutMarginsFromSafeArea = false - contentView.preservesSuperviewLayoutMargins = false - contentView.addSubview(cellView) - NSLayoutConstraint.constraintPinSubview(toSuperview: cellView) - cellView.addSubview(bottomText) - bottomText.textAlignment = .center - bottomText.font = MFFonts.mfFontTXRegular(11.0) - bottomText.topAnchor.constraint(equalTo: cellView.topAnchor, constant: 38).isActive = true - bottomText.leadingAnchor.constraint(equalTo: cellView.leadingAnchor).isActive = true - bottomText.trailingAnchor.constraint(equalTo: cellView.trailingAnchor).isActive = true - cellView.bottomAnchor.constraint(equalTo: bottomText.bottomAnchor).isActive = true - } - - public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - guard let collectionModel = model as? RadioSwatchItemModel else { return } - fillColor = collectionModel.color - isUserInteractionEnabled = collectionModel.enabled ?? true - isStrikeThrough = collectionModel.strikethrough ?? false - bottomText.text = collectionModel.text - } -} diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift new file mode 100644 index 00000000..f6054ebb --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift @@ -0,0 +1,167 @@ +// +// RadioSwatchItem.swift +// MVMCoreUI +// +// Created by Lekshmi S on 01/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +import UIKit + +open class RadioSwatchItem: Control { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public var bottomText = Label.createLabelRegularMicro(true) + public var isOutOfStock = false + public var fillColor: Color = Color(uiColor: .mvmBlue) + + private var circleLayer: CAShapeLayer? + private var selectedLayer: CALayer? + private var strikeLayer: CALayer? + private var maskLayer: CALayer? + + public var radioSwatchModel: RadioSwatchItemModel? { + return model as? RadioSwatchItemModel + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + open override func updateView(_ size: CGFloat) { + super.updateView(size) + bottomText.updateView(size) + layer.setNeedsDisplay() + } + + public override func setupView() { + super.setupView() + + addSubview(bottomText) + bottomText.textAlignment = .center + bottomText.topAnchor.constraint(equalTo: topAnchor, constant: 38).isActive = true + bottomText.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + bottomText.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + bottomAnchor.constraint(equalTo: bottomText.bottomAnchor).isActive = true + addTarget(self, action: #selector(selectSwatch), for: .touchUpInside) + } + + public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? RadioSwatchItemModel else { return } + fillColor = model.color + isSelected = model.selected ?? false + isEnabled = model.enabled ?? true + bottomText.text = model.text + isOutOfStock = model.strikethrough ?? false + } + + //------------------------------------------------------ + // MARK: - State Handling + //------------------------------------------------------ + + open override func draw(_ layer: CALayer, in ctx: CGContext) { + //Draw the swatch + circleLayer?.removeFromSuperlayer() + let circle = getCircle(color: UIColor.mvmBlack, thickness: 1) + layer.addSublayer(circle) + circleLayer = circle + + //Draw the strikethrough + strikeLayer?.removeFromSuperlayer() + if isOutOfStock { + let line = getStrikeThrough(color: UIColor.mvmCoolGray6, thickness: 1) + layer.addSublayer(line) + strikeLayer = line + } + + //Draw the selected layer + selectedLayer?.removeFromSuperlayer() + if isSelected { + let outerCircle = getSelectedLayer(color: UIColor.black, thickness: 1) + layer.addSublayer(outerCircle) + selectedLayer = outerCircle + bottomText.isHidden = false + } else { + circleLayer?.path = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)).cgPath + bottomText.isHidden = true + } + + //Handle Mask + maskLayer?.removeFromSuperlayer() + if !isEnabled { + let mask = getMaskLayer() + layer.mask = mask + maskLayer = mask + } + } + + open override func layoutSubviews() { + super.layoutSubviews() + // Accounts for any size changes + layer.setNeedsDisplay() + } + + @objc open func selectSwatch() { + isSelected = true + radioSwatchModel?.selected = isSelected + layer.setNeedsDisplay() + } + + @objc open func deselectSwatch() { + isSelected = false + radioSwatchModel?.selected = isSelected + layer.setNeedsDisplay() + } + + func getCircle(color: UIColor, thickness: CGFloat) -> CAShapeLayer { + let circle = CAShapeLayer() + circle.name = "innercircle" + circle.fillColor = fillColor.cgColor + circle.opacity = 1.0 + circle.lineWidth = thickness + circle.strokeColor = isEnabled ? color.cgColor : UIColor.mvmCoolGray6.cgColor + + let circlePath = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)) + circle.path = circlePath.cgPath + return circle + } + + func getStrikeThrough(color: UIColor, thickness: CGFloat) -> CAShapeLayer { + let strikeThrough = CAShapeLayer() + strikeThrough.name = "strikethrough" + strikeThrough.fillColor = nil + strikeThrough.opacity = 1.0 + strikeThrough.lineWidth = thickness + strikeThrough.strokeColor = color.cgColor + + let linePath = UIBezierPath() + linePath.move(to: CGPoint(x: 12, y: 30)) + linePath.addLine(to: CGPoint(x: 42, y: 0)) + strikeThrough.path = linePath.cgPath + return strikeThrough + } + + func getSelectedLayer(color: UIColor, thickness: CGFloat) -> CAShapeLayer { + circleLayer?.path = UIBezierPath(ovalIn: CGRect(x: 15, y: 4, width: 24, height: 24)).cgPath + let circle = CAShapeLayer() + circle.name = "outercircle" + circle.fillColor = UIColor.clear.cgColor + circle.opacity = 1.0 + circle.lineWidth = thickness + circle.strokeColor = color.cgColor + + let circlePath = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)) + circle.path = circlePath.cgPath + return circle + } + + func getMaskLayer() -> CALayer { + let mask = CALayer() + mask.backgroundColor = UIColor.white.cgColor + mask.opacity = 0.3 + mask.frame = bounds + return mask + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift new file mode 100644 index 00000000..b274d28d --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift @@ -0,0 +1,24 @@ +// +// RadioSwatchItemCollectionViewCell.swift +// MVMCoreUI +// +// Created by Lekshmi S on 13/04/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation +open class RadioSwatchItemCollectionViewCell: CollectionViewCell { + let radioSwatch = RadioSwatchItem() + + open override func setupView() { + super.setupView() + addMolecule(radioSwatch) + MVMCoreUIUtility.setMarginsFor(contentView, leading: 0, top: 0, trailing: 0, bottom: 0) + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + guard let model = model as? RadioSwatchItemModel else { return } + radioSwatch.set(with: model, delegateObject, additionalData) + self.isUserInteractionEnabled = model.enabled ?? true + } +} diff --git a/MVMCoreUI/Atomic/Atoms/RadioSwatches.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift similarity index 54% rename from MVMCoreUI/Atomic/Atoms/RadioSwatches.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift index a27af6c8..d73c749a 100644 --- a/MVMCoreUI/Atomic/Atoms/RadioSwatches.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift @@ -6,97 +6,97 @@ // Copyright © 2020 Verizon Wireless. All rights reserved. // -import UIKit +import Foundation open class RadioSwatches: View { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - public let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + public var collectionView: CollectionView! var swatches: [RadioSwatchItemModel]? - - public var selectedSwatchItem: RadioSwatchItemModel? { - get{ - guard let selectedItem = collectionView.indexPathsForSelectedItems?.first else {return nil} - return swatches?[selectedItem.item] - } - } + private var size: CGFloat? + private var delegateObject: MVMCoreUIDelegateObject? //------------------------------------------------------ // MARK: - Constraints //------------------------------------------------------ public var collectionViewHeight: NSLayoutConstraint? - public let cellSize: Double = 54.0 - public let spacing: Double = 10 + private let cellSize: CGFloat = 54.0 + private let itemSpacing: CGFloat = 8.0 //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- + open override func layoutSubviews() { + super.layoutSubviews() + // Accounts for any collection size changes + DispatchQueue.main.async { + self.collectionView.collectionViewLayout.invalidateLayout() + } + } + open override func setupView() { super.setupView() - guard collectionView.superview == nil else { - return - } - collectionView.translatesAutoresizingMaskIntoConstraints = false - collectionView.dataSource = self - collectionView.delegate = self - collectionView.showsHorizontalScrollIndicator = false - collectionView.backgroundColor = .clear - collectionView.isAccessibilityElement = false + collectionView = createCollectionView() addSubview(collectionView) NSLayoutConstraint.constraintPinSubview(toSuperview: collectionView) collectionViewHeight = collectionView.heightAnchor.constraint(equalToConstant: 100) collectionViewHeight?.isActive = true } - public override func updateView(_ size: CGFloat) { - DispatchQueue.main.async { - self.collectionView.collectionViewLayout.invalidateLayout() - self.collectionView.reloadData() - guard let selectedCell = self.swatches?.firstIndex(where: {$0.selected == true}) else { - return - } - self.collectionView.selectItem(at: IndexPath(item: selectedCell, section: 0), animated: true, scrollPosition: .centeredHorizontally) - } + @objc override open func updateView(_ size: CGFloat) { + super.updateView(size) + self.size = size + collectionView.updateView(size) } public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) + self.delegateObject = delegateObject + guard let radioSwatchesModel = model as? RadioSwatchesModel else { return } - collectionView.layer.borderColor = backgroundColor?.cgColor + swatches = radioSwatchesModel.swatches + FormValidator.setupValidation(for: radioSwatchesModel, delegate: delegateObject?.formHolderDelegate) registerCells() - setupLayout(with: radioSwatchesModel) - prepareMolecules(with: radioSwatchesModel) + setHeight() collectionView.reloadData() } //------------------------------------------------------ // MARK: - Methods //------------------------------------------------------ - func registerCells() { - collectionView.register(RadioSwatchItem.self, forCellWithReuseIdentifier: "RadioSwatchItemCollectionViewCell") + open func registerCells() { + collectionView.register(RadioSwatchItemCollectionViewCell.self, forCellWithReuseIdentifier: "RadioSwatchItemCollectionViewCell") } - func setupLayout(with radioSwatchesModel: RadioSwatchesModel?) { + /// Creates the collection view. + open func createCollectionView() -> CollectionView { + let collection = CollectionView(frame: .zero, collectionViewLayout: createCollectionViewLayout()) + collection.dataSource = self + collection.delegate = self + return collection + } + + /// Creates the layout for the collection. + open func createCollectionViewLayout() -> UICollectionViewLayout { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical - layout.minimumLineSpacing = CGFloat(spacing) - layout.minimumInteritemSpacing = CGFloat(spacing) - collectionView.collectionViewLayout = layout + layout.minimumLineSpacing = itemSpacing + layout.minimumInteritemSpacing = itemSpacing + return layout } - func prepareMolecules(with radioSwatchesModel: RadioSwatchesModel?) { - guard let newSwatches = radioSwatchesModel?.swatches else { - swatches = nil + open func setHeight() { + guard let swatches = swatches, swatches.count > 0 else { + collectionViewHeight?.constant = 0 return } - swatches = newSwatches + // Calculate the height let collectionViewWidth = UIScreen.main.bounds.width - (2 * MFStyler.defaultHorizontalPaddingForApplicationWidth()) - let swatchesInRow = Double(floor(Double(collectionViewWidth/60.0))) - let numberOfRows = floor(Double(swatches?.count ?? 1)/swatchesInRow) + 1.0 - let height = (numberOfRows * cellSize) + (spacing * (numberOfRows-1)) + let swatchesInRow = floor(CGFloat(collectionViewWidth/(cellSize + itemSpacing))) + let numberOfRows = ceil(CGFloat(swatches.count)/swatchesInRow) + let height = (numberOfRows * cellSize) + (itemSpacing * (numberOfRows-1)) collectionViewHeight?.constant = CGFloat(height) - collectionViewHeight?.isActive = true } } @@ -115,21 +115,28 @@ extension RadioSwatches: UICollectionViewDataSource { } open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let molecule = swatches?[indexPath.row], let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RadioSwatchItemCollectionViewCell", for: indexPath) as? RadioSwatchItem else { - return UICollectionViewCell() + guard let molecule = swatches?[indexPath.row], let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RadioSwatchItemCollectionViewCell", for: indexPath) as? RadioSwatchItemCollectionViewCell else { + fatalError() } - cell.set(with: molecule, nil, nil) + cell.radioSwatch.isUserInteractionEnabled = false + cell.set(with: molecule, delegateObject, nil) + cell.updateView(size ?? collectionView.bounds.width) + if molecule.selected ?? false { + collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredVertically) + } + cell.layoutIfNeeded() return cell } } extension RadioSwatches: UICollectionViewDelegate { public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - guard let swatchItem = swatches?[indexPath.row] else { return } - swatchItem.selected = true + guard let cell = collectionView.cellForItem(at: indexPath) as? RadioSwatchItemCollectionViewCell else { return } + cell.radioSwatch.selectSwatch() + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) } public func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { - guard let swatchItem = swatches?[indexPath.row] else { return } - swatchItem.selected = false + guard let cell = collectionView.cellForItem(at: indexPath) as? RadioSwatchItemCollectionViewCell else { return } + cell.radioSwatch.deselectSwatch() } } diff --git a/MVMCoreUI/Atomic/Atoms/RadioSwatchesModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift similarity index 86% rename from MVMCoreUI/Atomic/Atoms/RadioSwatchesModel.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift index e5e0ce11..965b72f6 100644 --- a/MVMCoreUI/Atomic/Atoms/RadioSwatchesModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift @@ -8,25 +8,39 @@ import Foundation -@objcMembers public class RadioSwatchesModel: MoleculeModelProtocol { +@objcMembers public class RadioSwatchesModel: MoleculeModelProtocol, FormFieldProtocol { public var backgroundColor: Color? public static var identifier: String = "radioSwatches" public var swatches: [RadioSwatchItemModel] + public var fieldKey: String? + public var groupName: String = FormValidator.defaultGroupName + public var baseValue: AnyHashable? - public init(swatches: [RadioSwatchItemModel]) { - self.swatches = swatches + /// Returns the fieldValue of the selected swatch, otherwise the text of selected swatch. + public func formFieldValue() -> AnyHashable? { + let selectedSwatch = swatches.first { (swatch) -> Bool in + return (swatch.selected ?? false) + } + return selectedSwatch?.fieldValue ?? selectedSwatch?.text } private enum CodingKeys: String, CodingKey { case moleculeName case backgroundColor case swatches + case fieldKey + case groupName } required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - self.swatches = try typeContainer.decode([RadioSwatchItemModel].self, forKey: .swatches) self.backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + self.swatches = try typeContainer.decode([RadioSwatchItemModel].self, forKey: .swatches) + fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) + if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { + self.groupName = groupName + } + baseValue = formFieldValue() } public func encode(to encoder: Encoder) throws { @@ -34,10 +48,12 @@ import Foundation try container.encode(moleculeName, forKey: .moleculeName) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encode(swatches, forKey: .swatches) + try container.encodeIfPresent(fieldKey, forKey: .fieldKey) + try container.encodeIfPresent(groupName, forKey: .groupName) } } -@objcMembers public class RadioSwatchItemModel: MoleculeModelProtocol, FormFieldProtocol { +@objcMembers public class RadioSwatchItemModel: MoleculeModelProtocol { public var backgroundColor: Color? public static var identifier: String = "radioSwatchItem" public var color: Color = Color(uiColor: .mvmBlue) @@ -47,24 +63,6 @@ import Foundation public var strikethrough: Bool? = false public var fieldKey: String? public var fieldValue: String? - public var baseValue: AnyHashable? - public var groupName: String = FormValidator.defaultGroupName - - public init(color: Color, text:String, selected: Bool, enabled: Bool, strikethrough: Bool, fieldKey: String, fieldValue: String, groupName:String) { - self.color = color - self.text = text - self.selected = selected - self.fieldValue = fieldValue - self.enabled = enabled - self.strikethrough = strikethrough - self.fieldKey = fieldKey - self.groupName = groupName - baseValue = selected - } - - public func formFieldValue() -> AnyHashable? { - return selected - } private enum CodingKeys: String, CodingKey { case moleculeName @@ -74,9 +72,7 @@ import Foundation case selected case enabled case strikethrough - case fieldKey case fieldValue - case groupName } required public init(from decoder: Decoder) throws { @@ -95,12 +91,7 @@ import Foundation if let strikethrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) { self.strikethrough = strikethrough } - baseValue = self.selected - fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) - if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { - self.groupName = groupName - } } public func encode(to encoder: Encoder) throws { @@ -112,9 +103,7 @@ import Foundation try container.encodeIfPresent(selected, forKey: .selected) try container.encodeIfPresent(enabled, forKey: .enabled) try container.encodeIfPresent(strikethrough, forKey: .strikethrough) - try container.encodeIfPresent(fieldKey, forKey: .fieldKey) try container.encodeIfPresent(fieldValue, forKey: .fieldValue) - try container.encodeIfPresent(groupName, forKey: .groupName) } } diff --git a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift index ab0d5b43..1b050aca 100644 --- a/MVMCoreUI/Atomic/MoleculeObjectMapping.swift +++ b/MVMCoreUI/Atomic/MoleculeObjectMapping.swift @@ -70,6 +70,7 @@ import Foundation MoleculeObjectMapping.shared()?.register(viewClass: RadioButton.self, viewModelClass: RadioButtonModel.self) 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) // Other Atoms MoleculeObjectMapping.shared()?.register(viewClass: ProgressBar.self, viewModelClass: ProgressBarModel.self) From 98ff848b33a0e73247f7c6e7e3c5093d6aac1861 Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Tue, 14 Apr 2020 15:18:29 +0530 Subject: [PATCH 09/12] Added stroke color changes for selected and not selected. --- MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift | 8 ++++---- .../Selectors/RadioSwatchItemCollectionViewCell.swift | 1 - MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift | 5 +++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift index f6054ebb..2b5c05f3 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift @@ -71,7 +71,7 @@ open class RadioSwatchItem: Control { //Draw the strikethrough strikeLayer?.removeFromSuperlayer() if isOutOfStock { - let line = getStrikeThrough(color: UIColor.mvmCoolGray6, thickness: 1) + let line = getStrikeThrough(color: UIColor.mvmBlack, thickness: 1) layer.addSublayer(line) strikeLayer = line } @@ -79,7 +79,7 @@ open class RadioSwatchItem: Control { //Draw the selected layer selectedLayer?.removeFromSuperlayer() if isSelected { - let outerCircle = getSelectedLayer(color: UIColor.black, thickness: 1) + let outerCircle = getSelectedLayer(color: UIColor.mvmBlack, thickness: 1) layer.addSublayer(outerCircle) selectedLayer = outerCircle bottomText.isHidden = false @@ -121,7 +121,7 @@ open class RadioSwatchItem: Control { circle.fillColor = fillColor.cgColor circle.opacity = 1.0 circle.lineWidth = thickness - circle.strokeColor = isEnabled ? color.cgColor : UIColor.mvmCoolGray6.cgColor + circle.strokeColor = isSelected ? color.cgColor : UIColor.mvmCoolGray6.cgColor let circlePath = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)) circle.path = circlePath.cgPath @@ -134,7 +134,7 @@ open class RadioSwatchItem: Control { strikeThrough.fillColor = nil strikeThrough.opacity = 1.0 strikeThrough.lineWidth = thickness - strikeThrough.strokeColor = color.cgColor + strikeThrough.strokeColor = isSelected ? color.cgColor : UIColor.mvmCoolGray6.cgColor let linePath = UIBezierPath() linePath.move(to: CGPoint(x: 12, y: 30)) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift index b274d28d..3d3100a8 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift @@ -19,6 +19,5 @@ open class RadioSwatchItemCollectionViewCell: CollectionViewCell { open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { guard let model = model as? RadioSwatchItemModel else { return } radioSwatch.set(with: model, delegateObject, additionalData) - self.isUserInteractionEnabled = model.enabled ?? true } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift index d73c749a..ed16fc39 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift @@ -114,6 +114,11 @@ extension RadioSwatches: UICollectionViewDataSource { return swatches?.count ?? 0 } + public func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { + guard let molecule = swatches?[indexPath.row] else {return true } + return molecule.enabled ?? true + } + open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let molecule = swatches?[indexPath.row], let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RadioSwatchItemCollectionViewCell", for: indexPath) as? RadioSwatchItemCollectionViewCell else { fatalError() From e1894bd74b9fe2f7307fb5b0fc6ac48fb1f34323 Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Thu, 16 Apr 2020 11:26:56 +0530 Subject: [PATCH 10/12] moved function, clean code --- MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift | 7 ++++--- MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift | 10 +++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift index 2b5c05f3..9117aee8 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift @@ -64,14 +64,14 @@ open class RadioSwatchItem: Control { open override func draw(_ layer: CALayer, in ctx: CGContext) { //Draw the swatch circleLayer?.removeFromSuperlayer() - let circle = getCircle(color: UIColor.mvmBlack, thickness: 1) + let circle = getCircle(color: .mvmBlack, thickness: 1) layer.addSublayer(circle) circleLayer = circle //Draw the strikethrough strikeLayer?.removeFromSuperlayer() if isOutOfStock { - let line = getStrikeThrough(color: UIColor.mvmBlack, thickness: 1) + let line = getStrikeThrough(color: .mvmBlack, thickness: 1) layer.addSublayer(line) strikeLayer = line } @@ -79,7 +79,7 @@ open class RadioSwatchItem: Control { //Draw the selected layer selectedLayer?.removeFromSuperlayer() if isSelected { - let outerCircle = getSelectedLayer(color: UIColor.mvmBlack, thickness: 1) + let outerCircle = getSelectedLayer(color: .mvmBlack, thickness: 1) layer.addSublayer(outerCircle) selectedLayer = outerCircle bottomText.isHidden = false @@ -104,6 +104,7 @@ open class RadioSwatchItem: Control { } @objc open func selectSwatch() { + guard isEnabled else { return } isSelected = true radioSwatchModel?.selected = isSelected layer.setNeedsDisplay() diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift index ed16fc39..2811da69 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift @@ -114,11 +114,6 @@ extension RadioSwatches: UICollectionViewDataSource { return swatches?.count ?? 0 } - public func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { - guard let molecule = swatches?[indexPath.row] else {return true } - return molecule.enabled ?? true - } - open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let molecule = swatches?[indexPath.row], let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RadioSwatchItemCollectionViewCell", for: indexPath) as? RadioSwatchItemCollectionViewCell else { fatalError() @@ -135,6 +130,11 @@ extension RadioSwatches: UICollectionViewDataSource { } extension RadioSwatches: UICollectionViewDelegate { + public func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { + guard let molecule = swatches?[indexPath.row] else {return true } + return molecule.enabled ?? true + } + public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let cell = collectionView.cellForItem(at: indexPath) as? RadioSwatchItemCollectionViewCell else { return } cell.radioSwatch.selectSwatch() From 135bf4c073df588a41751478f2b58451edc2a5a6 Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Fri, 17 Apr 2020 16:30:49 +0530 Subject: [PATCH 11/12] Code cleanup --- .../Atomic/Atoms/Selectors/RadioSwatchItem.swift | 12 ++++-------- .../RadioSwatchItemCollectionViewCell.swift | 2 +- .../Atomic/Atoms/Selectors/RadioSwatches.swift | 13 +++++++------ .../Atomic/Atoms/Selectors/RadioSwatchesModel.swift | 1 - 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift index 9117aee8..a6e6b802 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift @@ -13,7 +13,7 @@ open class RadioSwatchItem: Control { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - public var bottomText = Label.createLabelRegularMicro(true) + public let bottomText = Label.createLabelRegularMicro(true) public var isOutOfStock = false public var fillColor: Color = Color(uiColor: .mvmBlue) @@ -35,19 +35,16 @@ open class RadioSwatchItem: Control { layer.setNeedsDisplay() } - public override func setupView() { + open override func setupView() { super.setupView() addSubview(bottomText) bottomText.textAlignment = .center - bottomText.topAnchor.constraint(equalTo: topAnchor, constant: 38).isActive = true - bottomText.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - bottomText.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true - bottomAnchor.constraint(equalTo: bottomText.bottomAnchor).isActive = true + NSLayoutConstraint.constraintPinSubview(bottomText, pinTop: true, topConstant: 38, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 0, pinRight: true, rightConstant: 0) addTarget(self, action: #selector(selectSwatch), for: .touchUpInside) } - public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? RadioSwatchItemModel else { return } fillColor = model.color @@ -60,7 +57,6 @@ open class RadioSwatchItem: Control { //------------------------------------------------------ // MARK: - State Handling //------------------------------------------------------ - open override func draw(_ layer: CALayer, in ctx: CGContext) { //Draw the swatch circleLayer?.removeFromSuperlayer() diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift index 3d3100a8..a820a680 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift @@ -8,7 +8,7 @@ import Foundation open class RadioSwatchItemCollectionViewCell: CollectionViewCell { - let radioSwatch = RadioSwatchItem() + public let radioSwatch = RadioSwatchItem() open override func setupView() { super.setupView() diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift index 2811da69..f13dd331 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift @@ -13,9 +13,9 @@ open class RadioSwatches: View { // MARK: - Properties //-------------------------------------------------- public var collectionView: CollectionView! - var swatches: [RadioSwatchItemModel]? + public var swatches: [RadioSwatchItemModel]? private var size: CGFloat? - private var delegateObject: MVMCoreUIDelegateObject? + private var delegateObject: MVMCoreUIDelegateObject? //------------------------------------------------------ // MARK: - Constraints @@ -50,7 +50,7 @@ open class RadioSwatches: View { collectionView.updateView(size) } - public override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) self.delegateObject = delegateObject @@ -65,6 +65,7 @@ open class RadioSwatches: View { //------------------------------------------------------ // MARK: - Methods //------------------------------------------------------ + /// Registers the cells with the collection view open func registerCells() { collectionView.register(RadioSwatchItemCollectionViewCell.self, forCellWithReuseIdentifier: "RadioSwatchItemCollectionViewCell") } @@ -130,17 +131,17 @@ extension RadioSwatches: UICollectionViewDataSource { } extension RadioSwatches: UICollectionViewDelegate { - public func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { + open func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { guard let molecule = swatches?[indexPath.row] else {return true } return molecule.enabled ?? true } - public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let cell = collectionView.cellForItem(at: indexPath) as? RadioSwatchItemCollectionViewCell else { return } cell.radioSwatch.selectSwatch() _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) } - public func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { + open func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { guard let cell = collectionView.cellForItem(at: indexPath) as? RadioSwatchItemCollectionViewCell else { return } cell.radioSwatch.deselectSwatch() } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift index 965b72f6..d422d0db 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift @@ -61,7 +61,6 @@ import Foundation public var selected: Bool? = false public var enabled: Bool? = true public var strikethrough: Bool? = false - public var fieldKey: String? public var fieldValue: String? private enum CodingKeys: String, CodingKey { From 7471d2adf68375bf6a97cd948d5e20ff4f196342 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Fri, 17 Apr 2020 19:05:01 -0400 Subject: [PATCH 12/12] Separate radio swatch model file remove item from radio swatch name add in reset function to be consistent. Fix selected color issues with outer circle Center line Move height calculations to be dynamic minor encoding updates --- MVMCoreUI.xcodeproj/project.pbxproj | 20 +++--- ...adioSwatchItem.swift => RadioSwatch.swift} | 46 +++++++------ ...ft => RadioSwatchCollectionViewCell.swift} | 8 +-- .../Atoms/Selectors/RadioSwatchModel.swift | 64 ++++++++++++++++++ .../Atoms/Selectors/RadioSwatches.swift | 36 +++++----- .../Atoms/Selectors/RadioSwatchesModel.swift | 67 ++----------------- 6 files changed, 130 insertions(+), 111 deletions(-) rename MVMCoreUI/Atomic/Atoms/Selectors/{RadioSwatchItem.swift => RadioSwatch.swift} (77%) rename MVMCoreUI/Atomic/Atoms/Selectors/{RadioSwatchItemCollectionViewCell.swift => RadioSwatchCollectionViewCell.swift} (71%) create mode 100644 MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 077b0ca2..befe232c 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -190,11 +190,11 @@ AA1EC59924373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1EC59824373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift */; }; AA56A20F243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA56A20E243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift */; }; AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA56A210243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift */; }; - AA85236C244435A20059CC1E /* RadioSwatchItemCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA85236B244435A20059CC1E /* RadioSwatchItemCollectionViewCell.swift */; }; + AA85236C244435A20059CC1E /* RadioSwatchCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */; }; AAA74A172410C04600080241 /* HeadersH2NoButtonsBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */; }; AAA74A192410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */; }; AAB9C10824346F4B00151545 /* RadioSwatches.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB9C10724346F4B00151545 /* RadioSwatches.swift */; }; - AAB9C10A243496DD00151545 /* RadioSwatchItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB9C109243496DD00151545 /* RadioSwatchItem.swift */; }; + AAB9C10A243496DD00151545 /* RadioSwatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAB9C109243496DD00151545 /* RadioSwatch.swift */; }; AAC6F167243332E400F295C1 /* RadioSwatchesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */; }; BB2C968F24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C968D24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift */; }; BB2C969224330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB2C969124330F73006FF80C /* ListRightVariableTextLinkAllTextAndLinks.swift */; }; @@ -226,6 +226,7 @@ C7F8012323E846C300396FBD /* ListRVWheelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F8012223E846C300396FBD /* ListRVWheelModel.swift */; }; D202AFE4242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D202AFE3242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift */; }; D202AFE6242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D202AFE5242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift */; }; + D2092349244A51D40044AD09 /* RadioSwatchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2092348244A51D40044AD09 /* RadioSwatchModel.swift */; }; D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */; }; D20FB165241A5D75004AFC3A /* NavigationItemModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */; }; D213347723843825008E41B3 /* Line.swift in Sources */ = {isa = PBXBuildFile; fileRef = D213347623843825008E41B3 /* Line.swift */; }; @@ -620,11 +621,11 @@ AA1EC59824373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListThreeColumnSpeedTestDivider.swift; sourceTree = ""; }; AA56A20E243C5EE900303286 /* ListTwoColumnSubsectionDividerModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnSubsectionDividerModel.swift; sourceTree = ""; }; AA56A210243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTwoColumnSubsectionDivider.swift; sourceTree = ""; }; - AA85236B244435A20059CC1E /* RadioSwatchItemCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchItemCollectionViewCell.swift; sourceTree = ""; }; + AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchCollectionViewCell.swift; sourceTree = ""; }; AAA74A162410C04600080241 /* HeadersH2NoButtonsBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyText.swift; sourceTree = ""; }; AAA74A182410C05800080241 /* HeadersH2NoButtonsBodyTextModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadersH2NoButtonsBodyTextModel.swift; sourceTree = ""; }; AAB9C10724346F4B00151545 /* RadioSwatches.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatches.swift; sourceTree = ""; }; - AAB9C109243496DD00151545 /* RadioSwatchItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchItem.swift; sourceTree = ""; }; + AAB9C109243496DD00151545 /* RadioSwatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatch.swift; sourceTree = ""; }; AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchesModel.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 = ""; }; @@ -656,6 +657,7 @@ C7F8012223E846C300396FBD /* ListRVWheelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRVWheelModel.swift; sourceTree = ""; }; D202AFE3242A5F5E00E5BEDF /* NSTextAlignment+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSTextAlignment+Extension.swift"; sourceTree = ""; }; D202AFE5242A6A9C00E5BEDF /* UICollectionViewScrollPosition+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionViewScrollPosition+Extension.swift"; sourceTree = ""; }; + D2092348244A51D40044AD09 /* RadioSwatchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioSwatchModel.swift; sourceTree = ""; }; D20A9A5D2243D3E300ADE781 /* TwoButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoButtonView.swift; sourceTree = ""; }; D20FB164241A5D75004AFC3A /* NavigationItemModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItemModelProtocol.swift; sourceTree = ""; }; D213347623843825008E41B3 /* Line.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Line.swift; sourceTree = ""; }; @@ -1439,8 +1441,9 @@ 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */, AAC6F166243332E400F295C1 /* RadioSwatchesModel.swift */, AAB9C10724346F4B00151545 /* RadioSwatches.swift */, - AAB9C109243496DD00151545 /* RadioSwatchItem.swift */, - AA85236B244435A20059CC1E /* RadioSwatchItemCollectionViewCell.swift */, + D2092348244A51D40044AD09 /* RadioSwatchModel.swift */, + AAB9C109243496DD00151545 /* RadioSwatch.swift */, + AA85236B244435A20059CC1E /* RadioSwatchCollectionViewCell.swift */, ); path = Selectors; sourceTree = ""; @@ -2114,7 +2117,7 @@ 012A88AD238C418100FE3DA1 /* TemplateProtocol.swift in Sources */, BB6C6AC1242232DF005F7224 /* ListOneColumnTextWithWhitespaceDividerTall.swift in Sources */, D21B7F77243BB70700051ABF /* MoleculeCollectionItemModel.swift in Sources */, - AAB9C10A243496DD00151545 /* RadioSwatchItem.swift in Sources */, + AAB9C10A243496DD00151545 /* RadioSwatch.swift in Sources */, D29DF2B421E7B76D003B2FB9 /* MFLoadingSpinner.m in Sources */, 011D9602240DA20A000E3791 /* FormRuleWatcherFieldProtocol.swift in Sources */, D264FAA1243CF66B00D98315 /* ContainerCollectionReusableView.swift in Sources */, @@ -2219,6 +2222,7 @@ D260105D23D0BCD400764D80 /* Stack.swift in Sources */, 0A7EF85D23D8A95600B2AAD1 /* TextEntryFieldModel.swift in Sources */, BB54C5212434D92F0038326C /* ListRightVariableButtonAllTextAndLinksModel.swift in Sources */, + D2092349244A51D40044AD09 /* RadioSwatchModel.swift in Sources */, 8DD1E370243B3D0500D8F2DF /* ListThreeColumnInternationalData.swift in Sources */, D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */, 01EB368F23609801006832FA /* LabelModel.swift in Sources */, @@ -2333,7 +2337,7 @@ 0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */, AA56A211243C5EFC00303286 /* ListTwoColumnSubsectionDivider.swift in Sources */, D264FA8C243BCD8E00D98315 /* CollectionTemplateModel.swift in Sources */, - AA85236C244435A20059CC1E /* RadioSwatchItemCollectionViewCell.swift in Sources */, + AA85236C244435A20059CC1E /* RadioSwatchCollectionViewCell.swift in Sources */, 52B201D224081CFB00D2011E /* ListLeftVariableRadioButtonAndPaymentMethod.swift in Sources */, D26C5A6B23F4A40D007AEECE /* ListItemModel.swift in Sources */, 0A21DB8D235E06EF00C160A2 /* MFDigitTextField.m in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift similarity index 77% rename from MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift index a6e6b802..35294457 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItem.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatch.swift @@ -1,5 +1,5 @@ // -// RadioSwatchItem.swift +// RadioSwatch.swift // MVMCoreUI // // Created by Lekshmi S on 01/04/20. @@ -9,21 +9,19 @@ import Foundation import UIKit -open class RadioSwatchItem: Control { +open class RadioSwatch: Control { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- public let bottomText = Label.createLabelRegularMicro(true) - public var isOutOfStock = false - public var fillColor: Color = Color(uiColor: .mvmBlue) private var circleLayer: CAShapeLayer? private var selectedLayer: CALayer? private var strikeLayer: CALayer? private var maskLayer: CALayer? - public var radioSwatchModel: RadioSwatchItemModel? { - return model as? RadioSwatchItemModel + public var radioSwatchModel: RadioSwatchModel? { + return model as? RadioSwatchModel } //-------------------------------------------------- @@ -40,18 +38,23 @@ open class RadioSwatchItem: Control { addSubview(bottomText) bottomText.textAlignment = .center - NSLayoutConstraint.constraintPinSubview(bottomText, pinTop: true, topConstant: 38, pinBottom: false, bottomConstant: 0, pinLeft: true, leftConstant: 0, pinRight: true, rightConstant: 0) + NSLayoutConstraint.constraintPinSubview(bottomText, pinTop: true, topConstant: 38, pinBottom: true, bottomConstant: 0, pinLeft: true, leftConstant: 0, pinRight: true, rightConstant: 0) addTarget(self, action: #selector(selectSwatch), for: .touchUpInside) } open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) - guard let model = model as? RadioSwatchItemModel else { return } - fillColor = model.color - isSelected = model.selected ?? false - isEnabled = model.enabled ?? true + guard let model = model as? RadioSwatchModel else { return } + isSelected = model.selected + isEnabled = model.enabled bottomText.text = model.text - isOutOfStock = model.strikethrough ?? false + } + + public override func reset() { + super.reset() + isSelected = false + isEnabled = true + bottomText.text = nil } //------------------------------------------------------ @@ -60,14 +63,14 @@ open class RadioSwatchItem: Control { open override func draw(_ layer: CALayer, in ctx: CGContext) { //Draw the swatch circleLayer?.removeFromSuperlayer() - let circle = getCircle(color: .mvmBlack, thickness: 1) + let circle = getCircle(color: isSelected ? .mvmBlack : .mvmCoolGray6, thickness: 1) layer.addSublayer(circle) circleLayer = circle //Draw the strikethrough strikeLayer?.removeFromSuperlayer() - if isOutOfStock { - let line = getStrikeThrough(color: .mvmBlack, thickness: 1) + if radioSwatchModel?.strikethrough ?? false { + let line = getStrikeThrough(color: isSelected ? .mvmBlack : .mvmCoolGray6, thickness: 1) layer.addSublayer(line) strikeLayer = line } @@ -75,12 +78,11 @@ open class RadioSwatchItem: Control { //Draw the selected layer selectedLayer?.removeFromSuperlayer() if isSelected { - let outerCircle = getSelectedLayer(color: .mvmBlack, thickness: 1) + let outerCircle = getSelectedLayer(color: isSelected ? .mvmBlack : .mvmCoolGray6, thickness: 1) layer.addSublayer(outerCircle) selectedLayer = outerCircle bottomText.isHidden = false } else { - circleLayer?.path = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)).cgPath bottomText.isHidden = true } @@ -115,10 +117,10 @@ open class RadioSwatchItem: Control { func getCircle(color: UIColor, thickness: CGFloat) -> CAShapeLayer { let circle = CAShapeLayer() circle.name = "innercircle" - circle.fillColor = fillColor.cgColor + circle.fillColor = radioSwatchModel?.color.cgColor ?? UIColor.blue.cgColor circle.opacity = 1.0 circle.lineWidth = thickness - circle.strokeColor = isSelected ? color.cgColor : UIColor.mvmCoolGray6.cgColor + circle.strokeColor = color.cgColor let circlePath = UIBezierPath(ovalIn: CGRect(x: 12, y: 1, width: 30, height: 30)) circle.path = circlePath.cgPath @@ -131,11 +133,11 @@ open class RadioSwatchItem: Control { strikeThrough.fillColor = nil strikeThrough.opacity = 1.0 strikeThrough.lineWidth = thickness - strikeThrough.strokeColor = isSelected ? color.cgColor : UIColor.mvmCoolGray6.cgColor + strikeThrough.strokeColor = color.cgColor let linePath = UIBezierPath() - linePath.move(to: CGPoint(x: 12, y: 30)) - linePath.addLine(to: CGPoint(x: 42, y: 0)) + linePath.move(to: CGPoint(x: 12, y: 31)) + linePath.addLine(to: CGPoint(x: 42, y: 1)) strikeThrough.path = linePath.cgPath return strikeThrough } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchCollectionViewCell.swift similarity index 71% rename from MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift rename to MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchCollectionViewCell.swift index a820a680..8412dab2 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchItemCollectionViewCell.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchCollectionViewCell.swift @@ -1,5 +1,5 @@ // -// RadioSwatchItemCollectionViewCell.swift +// RadioSwatchCollectionViewCell.swift // MVMCoreUI // // Created by Lekshmi S on 13/04/20. @@ -7,8 +7,8 @@ // import Foundation -open class RadioSwatchItemCollectionViewCell: CollectionViewCell { - public let radioSwatch = RadioSwatchItem() +open class RadioSwatchCollectionViewCell: CollectionViewCell { + public let radioSwatch = RadioSwatch() open override func setupView() { super.setupView() @@ -17,7 +17,7 @@ open class RadioSwatchItemCollectionViewCell: CollectionViewCell { } open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - guard let model = model as? RadioSwatchItemModel else { return } + guard let model = model as? RadioSwatchModel else { return } radioSwatch.set(with: model, delegateObject, additionalData) } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift new file mode 100644 index 00000000..3aadd467 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchModel.swift @@ -0,0 +1,64 @@ +// +// RadioSwatchModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 4/17/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import Foundation + +@objcMembers public class RadioSwatchModel: MoleculeModelProtocol { + public static var identifier: String = "radioSwatch" + public var backgroundColor: Color? + public var color: Color = Color(uiColor: .mvmBlue) + public var text: String? + public var selected: Bool = false + public var enabled: Bool = true + public var strikethrough: Bool = false + public var fieldValue: String? + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case color + case text + case selected + case enabled + case strikethrough + case fieldValue + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) { + self.color = color + } + text = try typeContainer.decodeIfPresent(String.self, forKey: .text) + if let selected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected) { + self.selected = selected + } + if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { + self.enabled = enabled + } + if let strikethrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) { + self.strikethrough = strikethrough + } + fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encode(color, forKey: .color) + try container.encodeIfPresent(text, forKey: .text) + try container.encode(selected, forKey: .selected) + try container.encode(enabled, forKey: .enabled) + try container.encode(strikethrough, forKey: .strikethrough) + try container.encodeIfPresent(fieldValue, forKey: .fieldValue) + } +} + + diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift index f13dd331..8a6b8faf 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatches.swift @@ -13,7 +13,7 @@ open class RadioSwatches: View { // MARK: - Properties //-------------------------------------------------- public var collectionView: CollectionView! - public var swatches: [RadioSwatchItemModel]? + public var swatches: [RadioSwatchModel]? private var size: CGFloat? private var delegateObject: MVMCoreUIDelegateObject? @@ -30,6 +30,7 @@ open class RadioSwatches: View { open override func layoutSubviews() { super.layoutSubviews() // Accounts for any collection size changes + setHeight() DispatchQueue.main.async { self.collectionView.collectionViewLayout.invalidateLayout() } @@ -57,24 +58,18 @@ open class RadioSwatches: View { guard let radioSwatchesModel = model as? RadioSwatchesModel else { return } swatches = radioSwatchesModel.swatches FormValidator.setupValidation(for: radioSwatchesModel, delegate: delegateObject?.formHolderDelegate) - registerCells() - setHeight() collectionView.reloadData() } //------------------------------------------------------ // MARK: - Methods //------------------------------------------------------ - /// Registers the cells with the collection view - open func registerCells() { - collectionView.register(RadioSwatchItemCollectionViewCell.self, forCellWithReuseIdentifier: "RadioSwatchItemCollectionViewCell") - } - /// Creates the collection view. open func createCollectionView() -> CollectionView { let collection = CollectionView(frame: .zero, collectionViewLayout: createCollectionViewLayout()) collection.dataSource = self collection.delegate = self + collection.register(RadioSwatchCollectionViewCell.self, forCellWithReuseIdentifier: "RadioSwatchCollectionViewCell") return collection } @@ -93,10 +88,17 @@ open class RadioSwatches: View { return } // Calculate the height - let collectionViewWidth = UIScreen.main.bounds.width - (2 * MFStyler.defaultHorizontalPaddingForApplicationWidth()) - let swatchesInRow = floor(CGFloat(collectionViewWidth/(cellSize + itemSpacing))) + let swatchesInRow = floor(CGFloat(collectionView.bounds.width/(cellSize + itemSpacing))) let numberOfRows = ceil(CGFloat(swatches.count)/swatchesInRow) let height = (numberOfRows * cellSize) + (itemSpacing * (numberOfRows-1)) + + if let oldHeight = collectionViewHeight?.constant, + height != oldHeight { + // Notify delegate of height change, called async to avoid various race conditions caused while happening while laying out initially. + DispatchQueue.main.async { + self.delegateObject?.moleculeDelegate?.moleculeLayoutUpdated(self) + } + } collectionViewHeight?.constant = CGFloat(height) } } @@ -116,13 +118,14 @@ extension RadioSwatches: UICollectionViewDataSource { } open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let molecule = swatches?[indexPath.row], let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RadioSwatchItemCollectionViewCell", for: indexPath) as? RadioSwatchItemCollectionViewCell else { + guard let molecule = swatches?[indexPath.row], let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RadioSwatchCollectionViewCell", for: indexPath) as? RadioSwatchCollectionViewCell else { fatalError() } + cell.reset() cell.radioSwatch.isUserInteractionEnabled = false cell.set(with: molecule, delegateObject, nil) cell.updateView(size ?? collectionView.bounds.width) - if molecule.selected ?? false { + if molecule.selected { collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredVertically) } cell.layoutIfNeeded() @@ -132,17 +135,18 @@ extension RadioSwatches: UICollectionViewDataSource { extension RadioSwatches: UICollectionViewDelegate { open func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool { - guard let molecule = swatches?[indexPath.row] else {return true } - return molecule.enabled ?? true + guard let molecule = swatches?[indexPath.row] else { return false } + return molecule.enabled } open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - guard let cell = collectionView.cellForItem(at: indexPath) as? RadioSwatchItemCollectionViewCell else { return } + guard let cell = collectionView.cellForItem(at: indexPath) as? RadioSwatchCollectionViewCell else { return } cell.radioSwatch.selectSwatch() _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) } + open func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { - guard let cell = collectionView.cellForItem(at: indexPath) as? RadioSwatchItemCollectionViewCell else { return } + guard let cell = collectionView.cellForItem(at: indexPath) as? RadioSwatchCollectionViewCell else { return } cell.radioSwatch.deselectSwatch() } } diff --git a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift index d422d0db..db8e5d5d 100644 --- a/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Selectors/RadioSwatchesModel.swift @@ -9,9 +9,9 @@ import Foundation @objcMembers public class RadioSwatchesModel: MoleculeModelProtocol, FormFieldProtocol { - public var backgroundColor: Color? public static var identifier: String = "radioSwatches" - public var swatches: [RadioSwatchItemModel] + public var backgroundColor: Color? + public var swatches: [RadioSwatchModel] public var fieldKey: String? public var groupName: String = FormValidator.defaultGroupName public var baseValue: AnyHashable? @@ -19,7 +19,7 @@ import Foundation /// Returns the fieldValue of the selected swatch, otherwise the text of selected swatch. public func formFieldValue() -> AnyHashable? { let selectedSwatch = swatches.first { (swatch) -> Bool in - return (swatch.selected ?? false) + return swatch.selected } return selectedSwatch?.fieldValue ?? selectedSwatch?.text } @@ -34,8 +34,8 @@ import Foundation required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - self.backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) - self.swatches = try typeContainer.decode([RadioSwatchItemModel].self, forKey: .swatches) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + swatches = try typeContainer.decode([RadioSwatchModel].self, forKey: .swatches) fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { self.groupName = groupName @@ -49,61 +49,6 @@ import Foundation try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) try container.encode(swatches, forKey: .swatches) try container.encodeIfPresent(fieldKey, forKey: .fieldKey) - try container.encodeIfPresent(groupName, forKey: .groupName) + try container.encode(groupName, forKey: .groupName) } } - -@objcMembers public class RadioSwatchItemModel: MoleculeModelProtocol { - public var backgroundColor: Color? - public static var identifier: String = "radioSwatchItem" - public var color: Color = Color(uiColor: .mvmBlue) - public var text: String? - public var selected: Bool? = false - public var enabled: Bool? = true - public var strikethrough: Bool? = false - public var fieldValue: String? - - private enum CodingKeys: String, CodingKey { - case moleculeName - case backgroundColor - case color - case text - case selected - case enabled - case strikethrough - case fieldValue - } - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) - color = try typeContainer.decode(Color.self, forKey: .color) - if let text = try typeContainer.decodeIfPresent(String.self, forKey: .text) { - self.text = text - } - if let selected = try typeContainer.decodeIfPresent(Bool.self, forKey: .selected) { - self.selected = selected - } - if let enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { - self.enabled = enabled - } - if let strikethrough = try typeContainer.decodeIfPresent(Bool.self, forKey: .strikethrough) { - self.strikethrough = strikethrough - } - fieldValue = try typeContainer.decodeIfPresent(String.self, forKey: .fieldValue) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(moleculeName, forKey: .moleculeName) - try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) - try container.encode(color, forKey: .color) - try container.encodeIfPresent(text, forKey: .text) - try container.encodeIfPresent(selected, forKey: .selected) - try container.encodeIfPresent(enabled, forKey: .enabled) - try container.encodeIfPresent(strikethrough, forKey: .strikethrough) - try container.encodeIfPresent(fieldValue, forKey: .fieldValue) - } -} - -