From 63f225398bdb11dde1e6bede8f2a3a91219304b4 Mon Sep 17 00:00:00 2001 From: Lekshmi S Date: Tue, 7 Apr 2020 17:59:28 +0530 Subject: [PATCH] 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)