From 7fec6f540e944e02ce3a30714c8a9b0c1752e11c Mon Sep 17 00:00:00 2001 From: Xi Zhang Date: Fri, 5 Jul 2024 15:10:34 -0400 Subject: [PATCH 1/7] Add support for circular progress bar. --- MVMCoreUI.xcodeproj/project.pbxproj | 8 ++ .../Atoms/Views/CircularProgressBar.swift | 75 ++++++++++++++ .../Views/CircularProgressBarModel.swift | 99 +++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift create mode 100644 MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 3433e17b..0a1ea43d 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -153,6 +153,8 @@ 444FB7C32821B76B00DFE692 /* TitleLockupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444FB7C22821B76B00DFE692 /* TitleLockupModel.swift */; }; 4457904E27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4457904D27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift */; }; 4B002ACA2BD855EC009BC9C1 /* DateDropdownEntryFieldModel+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B002AC92BD855EC009BC9C1 /* DateDropdownEntryFieldModel+Extension.swift */; }; + 4B3408A22C3873B0003BFABF /* CircularProgressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3408A12C3873B0003BFABF /* CircularProgressBarModel.swift */; }; + 4B3408A42C3873E8003BFABF /* CircularProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3408A32C3873E8003BFABF /* CircularProgressBar.swift */; }; 522679C123FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */; }; 522679C223FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */; }; 52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */; }; @@ -770,6 +772,8 @@ 444FB7C22821B76B00DFE692 /* TitleLockupModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleLockupModel.swift; sourceTree = ""; }; 4457904D27ECE989002B1E1E /* UIImageRenderingMode+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageRenderingMode+Extension.swift"; sourceTree = ""; }; 4B002AC92BD855EC009BC9C1 /* DateDropdownEntryFieldModel+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateDropdownEntryFieldModel+Extension.swift"; sourceTree = ""; }; + 4B3408A12C3873B0003BFABF /* CircularProgressBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgressBarModel.swift; sourceTree = ""; }; + 4B3408A32C3873E8003BFABF /* CircularProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgressBar.swift; sourceTree = ""; }; 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinks.swift; sourceTree = ""; }; 522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinksModel.swift; sourceTree = ""; }; 52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextAllTextAndLinks.swift; sourceTree = ""; }; @@ -2311,6 +2315,8 @@ 0A7BAFA2232BE63400FB8E22 /* CheckboxLabel.swift */, D28A838223CCBD3F00DFE4FC /* WheelModel.swift */, 943784F3236B77BB006A1E82 /* Wheel.swift */, + 4B3408A12C3873B0003BFABF /* CircularProgressBarModel.swift */, + 4B3408A32C3873E8003BFABF /* CircularProgressBar.swift */, 943784F4236B77BB006A1E82 /* WheelAnimationHandler.swift */, 0AE98BB623FF18E9004C5109 /* ArrowModel.swift */, 0AE98BB423FF18D2004C5109 /* Arrow.swift */, @@ -3005,6 +3011,7 @@ D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */, D22479942316AE5E003FCCF9 /* NSLayoutConstraintExtension.swift in Sources */, D2B18B94236214AD00A9AEDC /* NavigationController.swift in Sources */, + 4B3408A42C3873E8003BFABF /* CircularProgressBar.swift in Sources */, 0A9D09222433796500D2E6C0 /* CarouselIndicator.swift in Sources */, EA17584E2BC9895A00A5C0D9 /* ButtonIcon.swift in Sources */, D29E28DA23D21AFA00ACEA85 /* StringAndMoleculeModel.swift in Sources */, @@ -3025,6 +3032,7 @@ AA1EC59924373994003D6F50 /* ListThreeColumnSpeedTestDivider.swift in Sources */, AA37CBD52519072F0027344C /* Stars.swift in Sources */, 942C378E2412F5B60066E45E /* ModalMoleculeStackTemplate.swift in Sources */, + 4B3408A22C3873B0003BFABF /* CircularProgressBarModel.swift in Sources */, 8D8067D32444473A00203BE8 /* ListRightVariablePriceChangeAllTextAndLinks.swift in Sources */, 8D4687E4242E2DF300802879 /* ListFourColumnDataUsageListItem.swift in Sources */, D2874024249BA6F300BE950A /* MVMCoreUISplitViewController+Extension.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift new file mode 100644 index 00000000..9383425a --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift @@ -0,0 +1,75 @@ +// +// CircularProgressBar.swift +// MVMCoreUI +// +// Created by Xi Zhang on 7/5/24. +// Copyright © 2024 Verizon Wireless. All rights reserved. +// + +import UIKit + +@objcMembers open class CircularProgressBar: View, MVMCoreUIViewConstrainingProtocol { + + var heightConstraint: NSLayoutConstraint? + weak var gradientLayer: CALayer? + var graphModel: CircularProgressBarModel? { + return model as? CircularProgressBarModel + } + +// MARK: setup + open override func setupView() { + super.setupView() + heightConstraint = heightAnchor.constraint(equalToConstant: 0) + heightConstraint?.isActive = true + widthAnchor.constraint(equalTo: heightAnchor).isActive = true + } + + override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + guard let model = model as? CircularProgressBarModel else { return } + createGraphCircle(model) + } + + class func getAngle(_ piValue: Double) -> Double { + return piValue / (2.0 * Double.pi) * 360.0 + } + + class func getPiValue(_ angle: Double) -> Double { + return angle / 360.0 * 2.0 * Double.pi + } + +// MARK: circle + open func createGraphCircle(_ graphObject: CircularProgressBarModel) { + if let sublayers = layer.sublayers { + for sublayer in sublayers { + sublayer.removeAllAnimations() + sublayer.removeFromSuperlayer() + } + } + heightConstraint?.constant = graphObject.diameter + + let gradient = CAGradientLayer() + gradient.type = .conic + gradient.startPoint = CGPoint(x: 0.5, y: 0.5) + gradient.endPoint = CGPoint(x: 0.5, y: 0.0) + gradient.frame = CGRect(x: 0, y: 0, width: graphObject.diameter, height: graphObject.diameter) + gradientLayer = gradient + layer.addSublayer(gradient) + + let center = CGPoint(x: gradient.bounds.midX, y: gradient.bounds.midY) + let radius = (graphObject.diameter - graphObject.lineWidth) / 2.0 + let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: (3 / 2 * .pi), endAngle: -(1 / 2 * .pi), clockwise: false) + let mask = CAShapeLayer() + mask.fillColor = UIColor.clear.cgColor + mask.strokeColor = UIColor.white.cgColor + mask.lineWidth = graphObject.lineWidth + mask.path = path.cgPath + gradient.mask = mask + } + +//MARK: MVMCoreUIViewConstrainingProtocol + public func needsToBeConstrained() -> Bool { + return true + } +} + diff --git a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift new file mode 100644 index 00000000..671607b1 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift @@ -0,0 +1,99 @@ +// +// CircularProgressBarModel.swift +// MVMCoreUI +// +// Created by Xi Zhang on 7/5/24. +// Copyright © 2024 Verizon Wireless. All rights reserved. +// + +import Foundation + + +public class CircularProgressBarModel: MoleculeModelProtocol { + + public static var identifier: String = "circularProgress" + public var id: String = UUID().uuidString + + public var size: GraphSize = .small { + didSet { + updateSize() + } + } + public var diameter: CGFloat = 84 + public var lineWidth: CGFloat = 5 + public var color: Color? + public var backgroundColor: Color? + public var percent: Int? + + public init() { + updateSize() + } + + private enum CodingKeys: String, CodingKey { + case id + case size + case diameter + case lineWidth + case color + case backgroundColor + case percent + case moleculeName + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString + + if let size = try typeContainer.decodeIfPresent(GraphSize.self, forKey: .size) { + self.size = size + } + updateSize() + + if let diameter = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .diameter) { + self.diameter = diameter + } + + if let lineWidth = try typeContainer.decodeIfPresent(CGFloat.self, forKey: .lineWidth) { + self.lineWidth = lineWidth + } + + color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + percent = try typeContainer.decodeIfPresent(Int.self, forKey: .percent) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(id, forKey: .id) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(size, forKey: .size) + try container.encode(diameter, forKey: .diameter) + try container.encode(lineWidth, forKey: .lineWidth) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(color, forKey: .color) + try container.encodeIfPresent(percent, forKey: .percent) + } + + func getCGColorsFromArray(_ colorArray: [String]) -> [Color] { + return colorArray.map { (colorString) -> Color in + return Color(uiColor: UIColor.mfGet(forHex: colorString)) + } + } + + func updateSize() { + switch size { + case .small: + diameter = MFSizeObject(standardSize: 20)?.getValueBasedOnApplicationWidth() ?? 20 + lineWidth = MFSizeObject(standardSize: 4)?.getValueBasedOnApplicationWidth() ?? 4 + break + case .medium: + diameter = MFSizeObject(standardSize: 100)?.getValueBasedOnApplicationWidth() ?? 100 + lineWidth = MFSizeObject(standardSize: 8)?.getValueBasedOnApplicationWidth() ?? 8 + break + case .large: + diameter = MFSizeObject(standardSize: 180)?.getValueBasedOnApplicationWidth() ?? 180 + lineWidth = MFSizeObject(standardSize: 12)?.getValueBasedOnApplicationWidth() ?? 12 + break + } + } +} From 1b0197ed2c180a4584e11ef6cd80fed3edb67c75 Mon Sep 17 00:00:00 2001 From: Xi Zhang Date: Tue, 9 Jul 2024 18:49:10 -0400 Subject: [PATCH 2/7] Add support for circular progress bar part 2 --- .../Atoms/Views/CircularProgressBar.swift | 120 ++++++++++++------ .../Views/CircularProgressBarModel.swift | 12 +- .../OtherHandlers/CoreUIModelMapping.swift | 1 + 3 files changed, 90 insertions(+), 43 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift index 9383425a..df5fe946 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift @@ -11,11 +11,82 @@ import UIKit @objcMembers open class CircularProgressBar: View, MVMCoreUIViewConstrainingProtocol { var heightConstraint: NSLayoutConstraint? - weak var gradientLayer: CALayer? var graphModel: CircularProgressBarModel? { return model as? CircularProgressBarModel } + private var progressLayer = CAShapeLayer() + private var tracklayer = CAShapeLayer() + private var labelLayer = CATextLayer() + + var setProgressColor: UIColor = UIColor.red { + didSet { + progressLayer.strokeColor = setProgressColor.cgColor + } + } + + var setTrackColor: UIColor = UIColor.white { + didSet { + tracklayer.strokeColor = setTrackColor.cgColor + } + } + /** + A path that consists of straight and curved line segments that you can render in your custom views. + Meaning our CAShapeLayer will now be drawn on the screen with the path we have specified here + */ + private var viewCGPath: CGPath? { + + let width = graphModel?.diameter ?? 84 + let height = width + + return UIBezierPath(arcCenter: CGPoint(x: width / 2.0, y: height / 2.0), + radius: (width - 1.5)/2, + startAngle: CGFloat(-0.5 * Double.pi), + endAngle: CGFloat(1.5 * Double.pi), clockwise: true).cgPath + } + + private func configureProgressViewToBeCircular() { + let lineWidth = graphModel?.lineWidth ?? 2.0 + self.backgroundColor = UIColor.clear + + self.drawShape(using: tracklayer, lineWidth: lineWidth) + self.drawShape(using: progressLayer, lineWidth: lineWidth) + } + + private func drawShape(using shape: CAShapeLayer, lineWidth: CGFloat) { + shape.path = self.viewCGPath + shape.fillColor = UIColor.clear.cgColor + shape.lineWidth = lineWidth + self.layer.addSublayer(shape) + } + + func setProgressWithAnimation(duration: TimeInterval, value: Float) { + let animation = CABasicAnimation(keyPath: "strokeEnd") + animation.duration = duration + + animation.fromValue = 0 //start animation at point 0 + animation.toValue = value //end animation at point specified + animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) + progressLayer.strokeEnd = CGFloat(value) + progressLayer.add(animation, forKey: "animateCircle") + } + + func drawLabel() { + + let percent = graphModel?.percent ?? 0 + let percentLen = percent > 9 ? 2 : 1 + let attributedString = NSMutableAttributedString(string: String(percent) + "%") + attributedString.setAttributes([NSAttributedString.Key.font: MFStyler.fontBoldTitleLarge()], range: NSMakeRange(0, percentLen)) + attributedString.setAttributes([NSAttributedString.Key.font: MFStyler.fontBoldBodyLarge()], range: NSMakeRange(percentLen, 1)) + + // Text layer + let width = graphModel?.diameter ?? 84 + let height = width + labelLayer.string = attributedString + labelLayer.frame = CGRectMake((width - CGFloat(percentLen * 20))/2, (height - 30)/2, 60, 30) + self.layer.addSublayer(labelLayer) + } + // MARK: setup open override func setupView() { super.setupView() @@ -27,46 +98,21 @@ import UIKit override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.set(with: model, delegateObject, additionalData) guard let model = model as? CircularProgressBarModel else { return } - createGraphCircle(model) - } - - class func getAngle(_ piValue: Double) -> Double { - return piValue / (2.0 * Double.pi) * 360.0 - } - - class func getPiValue(_ angle: Double) -> Double { - return angle / 360.0 * 2.0 * Double.pi - } - -// MARK: circle - open func createGraphCircle(_ graphObject: CircularProgressBarModel) { - if let sublayers = layer.sublayers { - for sublayer in sublayers { - sublayer.removeAllAnimations() - sublayer.removeFromSuperlayer() - } + + configureProgressViewToBeCircular() + + if let color = model.color { + setProgressColor = color.uiColor } - heightConstraint?.constant = graphObject.diameter - let gradient = CAGradientLayer() - gradient.type = .conic - gradient.startPoint = CGPoint(x: 0.5, y: 0.5) - gradient.endPoint = CGPoint(x: 0.5, y: 0.0) - gradient.frame = CGRect(x: 0, y: 0, width: graphObject.diameter, height: graphObject.diameter) - gradientLayer = gradient - layer.addSublayer(gradient) + if let backgroundColor = model.backgroundColor { + setTrackColor = backgroundColor.uiColor + } - let center = CGPoint(x: gradient.bounds.midX, y: gradient.bounds.midY) - let radius = (graphObject.diameter - graphObject.lineWidth) / 2.0 - let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: (3 / 2 * .pi), endAngle: -(1 / 2 * .pi), clockwise: false) - let mask = CAShapeLayer() - mask.fillColor = UIColor.clear.cgColor - mask.strokeColor = UIColor.white.cgColor - mask.lineWidth = graphObject.lineWidth - mask.path = path.cgPath - gradient.mask = mask + setProgressWithAnimation(duration: 0.5, value: Float(graphModel?.percent ?? 0) / 100) + drawLabel() } - + //MARK: MVMCoreUIViewConstrainingProtocol public func needsToBeConstrained() -> Bool { return true diff --git a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift index 671607b1..3a865b62 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift @@ -83,16 +83,16 @@ public class CircularProgressBarModel: MoleculeModelProtocol { func updateSize() { switch size { case .small: - diameter = MFSizeObject(standardSize: 20)?.getValueBasedOnApplicationWidth() ?? 20 - lineWidth = MFSizeObject(standardSize: 4)?.getValueBasedOnApplicationWidth() ?? 4 + diameter = MFSizeObject(standardSize: 64)?.getValueBasedOnApplicationWidth() ?? 64 + lineWidth = MFSizeObject(standardSize: 5)?.getValueBasedOnApplicationWidth() ?? 5 break case .medium: - diameter = MFSizeObject(standardSize: 100)?.getValueBasedOnApplicationWidth() ?? 100 - lineWidth = MFSizeObject(standardSize: 8)?.getValueBasedOnApplicationWidth() ?? 8 + diameter = MFSizeObject(standardSize: 84)?.getValueBasedOnApplicationWidth() ?? 84 + lineWidth = MFSizeObject(standardSize: 5)?.getValueBasedOnApplicationWidth() ?? 5 break case .large: - diameter = MFSizeObject(standardSize: 180)?.getValueBasedOnApplicationWidth() ?? 180 - lineWidth = MFSizeObject(standardSize: 12)?.getValueBasedOnApplicationWidth() ?? 12 + diameter = MFSizeObject(standardSize: 124)?.getValueBasedOnApplicationWidth() ?? 124 + lineWidth = MFSizeObject(standardSize: 5)?.getValueBasedOnApplicationWidth() ?? 5 break } } diff --git a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift index 22f65ade..d310a126 100644 --- a/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift +++ b/MVMCoreUI/OtherHandlers/CoreUIModelMapping.swift @@ -67,6 +67,7 @@ open class CoreUIModelMapping: ModelMapping { ModelRegistry.register(handler: LoadImageView.self, for: ImageViewModel.self) ModelRegistry.register(handler: Line.self, for: LineModel.self) ModelRegistry.register(handler: Wheel.self, for: WheelModel.self) + ModelRegistry.register(handler: CircularProgressBar.self, for: CircularProgressBarModel.self) ModelRegistry.register(handler: Toggle.self, for: ToggleModel.self) ModelRegistry.register(handler: CheckboxLabel.self, for: CheckboxLabelModel.self) ModelRegistry.register(handler: Arrow.self, for: ArrowModel.self) From 20d818b2c2df472e465bb563e89fdf7c6cbb91d7 Mon Sep 17 00:00:00 2001 From: Xi Zhang Date: Thu, 11 Jul 2024 13:12:29 -0400 Subject: [PATCH 3/7] Refactor codes for circular progress UI. --- .../Atoms/Views/CircularProgressBar.swift | 98 ++++++++++++------- .../Views/CircularProgressBarModel.swift | 15 ++- 2 files changed, 71 insertions(+), 42 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift index df5fe946..011359c8 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift @@ -15,6 +15,10 @@ import UIKit return model as? CircularProgressBarModel } + var viewWidth: CGFloat { + graphModel?.diameter ?? CGFloat(84) + } + private var progressLayer = CAShapeLayer() private var tracklayer = CAShapeLayer() private var labelLayer = CATextLayer() @@ -25,18 +29,16 @@ import UIKit } } - var setTrackColor: UIColor = UIColor.white { + var setTrackColor: UIColor = UIColor.lightGray { didSet { tracklayer.strokeColor = setTrackColor.cgColor } } - /** - A path that consists of straight and curved line segments that you can render in your custom views. - Meaning our CAShapeLayer will now be drawn on the screen with the path we have specified here - */ + + // A path with which CAShapeLayer will be drawn on the screen private var viewCGPath: CGPath? { - let width = graphModel?.diameter ?? 84 + let width = viewWidth let height = width return UIBezierPath(arcCenter: CGPoint(x: width / 2.0, y: height / 2.0), @@ -45,9 +47,51 @@ import UIKit endAngle: CGFloat(1.5 * Double.pi), clockwise: true).cgPath } +// MARK: setup + override open func setupView() { + super.setupView() + heightConstraint = heightAnchor.constraint(equalToConstant: 0) + heightConstraint?.isActive = true + widthAnchor.constraint(equalTo: heightAnchor).isActive = true + } + + override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + + super.set(with: model, delegateObject, additionalData) + guard let model = model as? CircularProgressBarModel else { return } + + // set background color + if let backgroundColor = model.backgroundColor { + self.backgroundColor = backgroundColor.uiColor + } else { + self.backgroundColor = UIColor.clear + } + + configureProgressViewToBeCircular() + + // set progress color + if let color = model.color { + setProgressColor = color.uiColor + } else { + setProgressColor = UIColor.red + } + + // set track color + if let trackColor = model.trackColor { + setTrackColor = trackColor.uiColor + } else { + setProgressColor = UIColor.lightGray + } + + // show circular progress view with animation. + showProgressWithAnimation(duration: 0.5, value: Float(graphModel?.percent ?? 0) / 100) + + // show progress percentage label. + showProgressPercentage() + } + private func configureProgressViewToBeCircular() { - let lineWidth = graphModel?.lineWidth ?? 2.0 - self.backgroundColor = UIColor.clear + let lineWidth = graphModel?.lineWidth ?? 5.0 self.drawShape(using: tracklayer, lineWidth: lineWidth) self.drawShape(using: progressLayer, lineWidth: lineWidth) @@ -60,7 +104,8 @@ import UIKit self.layer.addSublayer(shape) } - func setProgressWithAnimation(duration: TimeInterval, value: Float) { + // value range is [0,1] + private func showProgressWithAnimation(duration: TimeInterval, value: Float) { let animation = CABasicAnimation(keyPath: "strokeEnd") animation.duration = duration @@ -71,47 +116,26 @@ import UIKit progressLayer.add(animation, forKey: "animateCircle") } - func drawLabel() { + private func showProgressPercentage() { let percent = graphModel?.percent ?? 0 let percentLen = percent > 9 ? 2 : 1 + + // configure attributed string for progress percentage. let attributedString = NSMutableAttributedString(string: String(percent) + "%") + // percent value attributedString.setAttributes([NSAttributedString.Key.font: MFStyler.fontBoldTitleLarge()], range: NSMakeRange(0, percentLen)) + // % symbol attributedString.setAttributes([NSAttributedString.Key.font: MFStyler.fontBoldBodyLarge()], range: NSMakeRange(percentLen, 1)) - // Text layer - let width = graphModel?.diameter ?? 84 + // show progress percentage in a text layer + let width = viewWidth let height = width labelLayer.string = attributedString labelLayer.frame = CGRectMake((width - CGFloat(percentLen * 20))/2, (height - 30)/2, 60, 30) self.layer.addSublayer(labelLayer) } -// MARK: setup - open override func setupView() { - super.setupView() - heightConstraint = heightAnchor.constraint(equalToConstant: 0) - heightConstraint?.isActive = true - widthAnchor.constraint(equalTo: heightAnchor).isActive = true - } - - override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - super.set(with: model, delegateObject, additionalData) - guard let model = model as? CircularProgressBarModel else { return } - - configureProgressViewToBeCircular() - - if let color = model.color { - setProgressColor = color.uiColor - } - - if let backgroundColor = model.backgroundColor { - setTrackColor = backgroundColor.uiColor - } - - setProgressWithAnimation(duration: 0.5, value: Float(graphModel?.percent ?? 0) / 100) - drawLabel() - } //MARK: MVMCoreUIViewConstrainingProtocol public func needsToBeConstrained() -> Bool { diff --git a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift index 3a865b62..6c58be43 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift @@ -22,9 +22,10 @@ public class CircularProgressBarModel: MoleculeModelProtocol { public var diameter: CGFloat = 84 public var lineWidth: CGFloat = 5 public var color: Color? - public var backgroundColor: Color? + public var trackColor: Color? public var percent: Int? - + public var backgroundColor: Color? = Color(uiColor: UIColor.clear) + public init() { updateSize() } @@ -35,8 +36,9 @@ public class CircularProgressBarModel: MoleculeModelProtocol { case diameter case lineWidth case color - case backgroundColor + case trackColor case percent + case backgroundColor case moleculeName } @@ -58,8 +60,10 @@ public class CircularProgressBarModel: MoleculeModelProtocol { } color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) - backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + trackColor = try typeContainer.decodeIfPresent(Color.self, forKey: .trackColor) percent = try typeContainer.decodeIfPresent(Int.self, forKey: .percent) + + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) } public func encode(to encoder: Encoder) throws { @@ -69,9 +73,10 @@ public class CircularProgressBarModel: MoleculeModelProtocol { try container.encode(size, forKey: .size) try container.encode(diameter, forKey: .diameter) try container.encode(lineWidth, forKey: .lineWidth) - try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encodeIfPresent(trackColor, forKey: .trackColor) try container.encodeIfPresent(color, forKey: .color) try container.encodeIfPresent(percent, forKey: .percent) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) } func getCGColorsFromArray(_ colorArray: [String]) -> [Color] { From de60fdfaf93085a185b343f05299c41fba5cdb83 Mon Sep 17 00:00:00 2001 From: Xi Zhang Date: Thu, 11 Jul 2024 17:22:11 -0400 Subject: [PATCH 4/7] Make animation duration configurable. --- MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift | 2 +- .../Atomic/Atoms/Views/CircularProgressBarModel.swift | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift index 011359c8..651b7f65 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift @@ -84,7 +84,7 @@ import UIKit } // show circular progress view with animation. - showProgressWithAnimation(duration: 0.5, value: Float(graphModel?.percent ?? 0) / 100) + showProgressWithAnimation(duration: graphModel?.duration ?? 1.0, value: Float(graphModel?.percent ?? 0) / 100) // show progress percentage label. showProgressPercentage() diff --git a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift index 6c58be43..252f4893 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift @@ -21,6 +21,7 @@ public class CircularProgressBarModel: MoleculeModelProtocol { } public var diameter: CGFloat = 84 public var lineWidth: CGFloat = 5 + public var duration : Double = 1.0 public var color: Color? public var trackColor: Color? public var percent: Int? @@ -35,6 +36,7 @@ public class CircularProgressBarModel: MoleculeModelProtocol { case size case diameter case lineWidth + case duration case color case trackColor case percent @@ -59,6 +61,10 @@ public class CircularProgressBarModel: MoleculeModelProtocol { self.lineWidth = lineWidth } + if let duration = try typeContainer.decodeIfPresent(Double.self, forKey: .duration) { + self.duration = duration + } + color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) trackColor = try typeContainer.decodeIfPresent(Color.self, forKey: .trackColor) percent = try typeContainer.decodeIfPresent(Int.self, forKey: .percent) @@ -73,6 +79,7 @@ public class CircularProgressBarModel: MoleculeModelProtocol { try container.encode(size, forKey: .size) try container.encode(diameter, forKey: .diameter) try container.encode(lineWidth, forKey: .lineWidth) + try container.encode(duration, forKey: .duration) try container.encodeIfPresent(trackColor, forKey: .trackColor) try container.encodeIfPresent(color, forKey: .color) try container.encodeIfPresent(percent, forKey: .percent) From 53c4bd6c466529fd8191089924d499ebcaeece32 Mon Sep 17 00:00:00 2001 From: Xi Zhang Date: Fri, 12 Jul 2024 16:12:54 -0400 Subject: [PATCH 5/7] Revise codes following MR comments --- .../Atoms/Views/CircularProgressBar.swift | 41 ++++------- .../Views/CircularProgressBarModel.swift | 68 +++++++++++-------- 2 files changed, 52 insertions(+), 57 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift index 651b7f65..bc9083d0 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift @@ -23,17 +23,8 @@ import UIKit private var tracklayer = CAShapeLayer() private var labelLayer = CATextLayer() - var setProgressColor: UIColor = UIColor.red { - didSet { - progressLayer.strokeColor = setProgressColor.cgColor - } - } - - var setTrackColor: UIColor = UIColor.lightGray { - didSet { - tracklayer.strokeColor = setTrackColor.cgColor - } - } + var progressColor: UIColor = UIColor.red + var trackColor: UIColor = UIColor.lightGray // A path with which CAShapeLayer will be drawn on the screen private var viewCGPath: CGPath? { @@ -61,37 +52,29 @@ import UIKit guard let model = model as? CircularProgressBarModel else { return } // set background color - if let backgroundColor = model.backgroundColor { - self.backgroundColor = backgroundColor.uiColor - } else { - self.backgroundColor = UIColor.clear - } + backgroundColor = model.backgroundColor?.uiColor ?? UIColor.clear configureProgressViewToBeCircular() // set progress color - if let color = model.color { - setProgressColor = color.uiColor - } else { - setProgressColor = UIColor.red - } - + progressColor = model.color?.uiColor ?? .red + progressLayer.strokeColor = progressColor.cgColor + // set track color - if let trackColor = model.trackColor { - setTrackColor = trackColor.uiColor - } else { - setProgressColor = UIColor.lightGray - } + trackColor = model.trackColor?.uiColor ?? .lightGray + tracklayer.strokeColor = trackColor.cgColor // show circular progress view with animation. showProgressWithAnimation(duration: graphModel?.duration ?? 1.0, value: Float(graphModel?.percent ?? 0) / 100) // show progress percentage label. - showProgressPercentage() + if let drawText = model.drawText, drawText { + showProgressPercentage() + } } private func configureProgressViewToBeCircular() { - let lineWidth = graphModel?.lineWidth ?? 5.0 + let lineWidth = graphModel?.lineWidth ?? 4.0 self.drawShape(using: tracklayer, lineWidth: lineWidth) self.drawShape(using: progressLayer, lineWidth: lineWidth) diff --git a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift index 252f4893..7e2f4cbd 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift @@ -2,29 +2,31 @@ // CircularProgressBarModel.swift // MVMCoreUI // +// https://oneconfluence.verizon.com/display/MFD/Circular+Progress+Tracker +// // Created by Xi Zhang on 7/5/24. // Copyright © 2024 Verizon Wireless. All rights reserved. // import Foundation - public class CircularProgressBarModel: MoleculeModelProtocol { public static var identifier: String = "circularProgress" public var id: String = UUID().uuidString - public var size: GraphSize = .small { + public var percent: Int = 0 + public var size: GraphSize? = .small { didSet { updateSize() } } - public var diameter: CGFloat = 84 - public var lineWidth: CGFloat = 5 - public var duration : Double = 1.0 - public var color: Color? - public var trackColor: Color? - public var percent: Int? + public var diameter: CGFloat? = 84 + public var lineWidth: CGFloat? = 4 + public var duration : Double? = 1.0 + public var color: Color? = Color(uiColor: UIColor.mfGet(forHex: "#007AB8")) + public var trackColor: Color? = Color(uiColor: .mvmCoolGray3) + public var drawText: Bool? = true public var backgroundColor: Color? = Color(uiColor: UIColor.clear) public init() { @@ -33,21 +35,24 @@ public class CircularProgressBarModel: MoleculeModelProtocol { private enum CodingKeys: String, CodingKey { case id + case moleculeName + case percent case size case diameter case lineWidth case duration case color case trackColor - case percent + case drawText case backgroundColor - case moleculeName } required public init(from decoder: Decoder) throws { let typeContainer = try decoder.container(keyedBy: CodingKeys.self) id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString + percent = try typeContainer.decode(Int.self, forKey: .percent) + if let size = try typeContainer.decodeIfPresent(GraphSize.self, forKey: .size) { self.size = size } @@ -65,46 +70,53 @@ public class CircularProgressBarModel: MoleculeModelProtocol { self.duration = duration } - color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) - trackColor = try typeContainer.decodeIfPresent(Color.self, forKey: .trackColor) - percent = try typeContainer.decodeIfPresent(Int.self, forKey: .percent) + if let drawText = try typeContainer.decodeIfPresent(Bool.self, forKey: .drawText) { + self.drawText = drawText + } - backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + if let color = try typeContainer.decodeIfPresent(Color.self, forKey: .color) { + self.color = color + } + + if let trackColor = try typeContainer.decodeIfPresent(Color.self, forKey: .trackColor) { + self.trackColor = trackColor + } + + if let backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) { + self.backgroundColor = backgroundColor + } } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(moleculeName, forKey: .moleculeName) - try container.encode(size, forKey: .size) - try container.encode(diameter, forKey: .diameter) - try container.encode(lineWidth, forKey: .lineWidth) - try container.encode(duration, forKey: .duration) + try container.encode(percent, forKey: .percent) + try container.encodeIfPresent(size, forKey: .size) + try container.encodeIfPresent(diameter, forKey: .diameter) + try container.encodeIfPresent(lineWidth, forKey: .lineWidth) + try container.encodeIfPresent(duration, forKey: .duration) + try container.encodeIfPresent(drawText, forKey: .drawText) try container.encodeIfPresent(trackColor, forKey: .trackColor) try container.encodeIfPresent(color, forKey: .color) - try container.encodeIfPresent(percent, forKey: .percent) try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) } - func getCGColorsFromArray(_ colorArray: [String]) -> [Color] { - return colorArray.map { (colorString) -> Color in - return Color(uiColor: UIColor.mfGet(forHex: colorString)) - } - } - func updateSize() { switch size { case .small: diameter = MFSizeObject(standardSize: 64)?.getValueBasedOnApplicationWidth() ?? 64 - lineWidth = MFSizeObject(standardSize: 5)?.getValueBasedOnApplicationWidth() ?? 5 + lineWidth = MFSizeObject(standardSize: 4)?.getValueBasedOnApplicationWidth() ?? 4 break case .medium: diameter = MFSizeObject(standardSize: 84)?.getValueBasedOnApplicationWidth() ?? 84 - lineWidth = MFSizeObject(standardSize: 5)?.getValueBasedOnApplicationWidth() ?? 5 + lineWidth = MFSizeObject(standardSize: 4)?.getValueBasedOnApplicationWidth() ?? 4 break case .large: diameter = MFSizeObject(standardSize: 124)?.getValueBasedOnApplicationWidth() ?? 124 - lineWidth = MFSizeObject(standardSize: 5)?.getValueBasedOnApplicationWidth() ?? 5 + lineWidth = MFSizeObject(standardSize: 4)?.getValueBasedOnApplicationWidth() ?? 4 + break + case .none: break } } From 5cbd472a2da34508e6ccd0a25ab83c42d0d809a4 Mon Sep 17 00:00:00 2001 From: Xi Zhang Date: Mon, 15 Jul 2024 16:04:34 -0400 Subject: [PATCH 6/7] Make diameter default value to 64 consistent with the default small size diameter value. --- MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift | 2 +- MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift index bc9083d0..f55e85c8 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBar.swift @@ -16,7 +16,7 @@ import UIKit } var viewWidth: CGFloat { - graphModel?.diameter ?? CGFloat(84) + graphModel?.diameter ?? CGFloat(64) } private var progressLayer = CAShapeLayer() diff --git a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift index 7e2f4cbd..e92dddbf 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift @@ -21,7 +21,7 @@ public class CircularProgressBarModel: MoleculeModelProtocol { updateSize() } } - public var diameter: CGFloat? = 84 + public var diameter: CGFloat? = 64 public var lineWidth: CGFloat? = 4 public var duration : Double? = 1.0 public var color: Color? = Color(uiColor: UIColor.mfGet(forHex: "#007AB8")) From 29466e4e336aab101c29be67114d6939fbb879f0 Mon Sep 17 00:00:00 2001 From: Xi Zhang Date: Mon, 15 Jul 2024 17:02:28 -0400 Subject: [PATCH 7/7] Move GraphSize to a higher level for common use. --- MVMCoreUI.xcodeproj/project.pbxproj | 4 +++ .../Views/CircularProgressBarModel.swift | 16 ++++------ .../Atoms/Views/GraphSizeProtocol.swift | 29 +++++++++++++++++++ MVMCoreUI/Atomic/Atoms/Views/WheelModel.swift | 17 ++++------- 4 files changed, 44 insertions(+), 22 deletions(-) create mode 100644 MVMCoreUI/Atomic/Atoms/Views/GraphSizeProtocol.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 0a1ea43d..12d16ced 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -155,6 +155,7 @@ 4B002ACA2BD855EC009BC9C1 /* DateDropdownEntryFieldModel+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B002AC92BD855EC009BC9C1 /* DateDropdownEntryFieldModel+Extension.swift */; }; 4B3408A22C3873B0003BFABF /* CircularProgressBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3408A12C3873B0003BFABF /* CircularProgressBarModel.swift */; }; 4B3408A42C3873E8003BFABF /* CircularProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3408A32C3873E8003BFABF /* CircularProgressBar.swift */; }; + 4B53AF7B2C45BBBA00274685 /* GraphSizeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B53AF7A2C45BBBA00274685 /* GraphSizeProtocol.swift */; }; 522679C123FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */; }; 522679C223FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */; }; 52267A0723FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */; }; @@ -774,6 +775,7 @@ 4B002AC92BD855EC009BC9C1 /* DateDropdownEntryFieldModel+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DateDropdownEntryFieldModel+Extension.swift"; sourceTree = ""; }; 4B3408A12C3873B0003BFABF /* CircularProgressBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgressBarModel.swift; sourceTree = ""; }; 4B3408A32C3873E8003BFABF /* CircularProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgressBar.swift; sourceTree = ""; }; + 4B53AF7A2C45BBBA00274685 /* GraphSizeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphSizeProtocol.swift; sourceTree = ""; }; 522679BF23FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinks.swift; sourceTree = ""; }; 522679C023FE886900906CBA /* ListLeftVariableCheckboxAllTextAndLinksModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListLeftVariableCheckboxAllTextAndLinksModel.swift; sourceTree = ""; }; 52267A0623FFE25000906CBA /* ListOneColumnFullWidthTextAllTextAndLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListOneColumnFullWidthTextAllTextAndLinks.swift; sourceTree = ""; }; @@ -2313,6 +2315,7 @@ 94C2D9822386F3E30006CF46 /* Label */, 31BE15C923D8924C00452370 /* CheckboxLabelModel.swift */, 0A7BAFA2232BE63400FB8E22 /* CheckboxLabel.swift */, + 4B53AF7A2C45BBBA00274685 /* GraphSizeProtocol.swift */, D28A838223CCBD3F00DFE4FC /* WheelModel.swift */, 943784F3236B77BB006A1E82 /* Wheel.swift */, 4B3408A12C3873B0003BFABF /* CircularProgressBarModel.swift */, @@ -3109,6 +3112,7 @@ D2A6390522CBCE160052ED1F /* MoleculeCollectionViewCell.swift in Sources */, D2A6390122CBB1820052ED1F /* Carousel.swift in Sources */, C7F8012123E8303200396FBD /* ListRVWheel.swift in Sources */, + 4B53AF7B2C45BBBA00274685 /* GraphSizeProtocol.swift in Sources */, BB2C968F24330EA7006FF80C /* ListRightVariableTextLinkAllTextAndLinksModel.swift in Sources */, D2FB151B23A2B65B00C20E10 /* MoleculeContainer.swift in Sources */, EA7D81622B2B6E7F00D29F9E /* IconModel.swift in Sources */, diff --git a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift index e92dddbf..61c503f6 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/CircularProgressBarModel.swift @@ -10,17 +10,12 @@ import Foundation -public class CircularProgressBarModel: MoleculeModelProtocol { +public class CircularProgressBarModel: GraphSizeBase, MoleculeModelProtocol { public static var identifier: String = "circularProgress" public var id: String = UUID().uuidString public var percent: Int = 0 - public var size: GraphSize? = .small { - didSet { - updateSize() - } - } public var diameter: CGFloat? = 64 public var lineWidth: CGFloat? = 4 public var duration : Double? = 1.0 @@ -29,7 +24,8 @@ public class CircularProgressBarModel: MoleculeModelProtocol { public var drawText: Bool? = true public var backgroundColor: Color? = Color(uiColor: UIColor.clear) - public init() { + public override init() { + super.init() updateSize() } @@ -48,6 +44,8 @@ public class CircularProgressBarModel: MoleculeModelProtocol { } required public init(from decoder: Decoder) throws { + + super.init() let typeContainer = try decoder.container(keyedBy: CodingKeys.self) id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString @@ -102,7 +100,7 @@ public class CircularProgressBarModel: MoleculeModelProtocol { try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) } - func updateSize() { + public override func updateSize() { switch size { case .small: diameter = MFSizeObject(standardSize: 64)?.getValueBasedOnApplicationWidth() ?? 64 @@ -116,8 +114,6 @@ public class CircularProgressBarModel: MoleculeModelProtocol { diameter = MFSizeObject(standardSize: 124)?.getValueBasedOnApplicationWidth() ?? 124 lineWidth = MFSizeObject(standardSize: 4)?.getValueBasedOnApplicationWidth() ?? 4 break - case .none: - break } } } diff --git a/MVMCoreUI/Atomic/Atoms/Views/GraphSizeProtocol.swift b/MVMCoreUI/Atomic/Atoms/Views/GraphSizeProtocol.swift new file mode 100644 index 00000000..13000706 --- /dev/null +++ b/MVMCoreUI/Atomic/Atoms/Views/GraphSizeProtocol.swift @@ -0,0 +1,29 @@ +// +// GraphSizeProtocol.swift +// MVMCoreUI +// +// Created by Xi Zhang on 7/15/24. +// Copyright © 2024 Verizon Wireless. All rights reserved. +// + +import Foundation + +public enum GraphSize: String, Codable { + case small, medium, large +} + +public protocol GraphSizeProtocol { + var size: GraphSize { get set } + func updateSize() +} + +public class GraphSizeBase: GraphSizeProtocol { + public var size: GraphSize = .small { + didSet { + updateSize() + } + } + + public func updateSize() { + } +} diff --git a/MVMCoreUI/Atomic/Atoms/Views/WheelModel.swift b/MVMCoreUI/Atomic/Atoms/Views/WheelModel.swift index 4fed14cb..416a1ac2 100644 --- a/MVMCoreUI/Atomic/Atoms/Views/WheelModel.swift +++ b/MVMCoreUI/Atomic/Atoms/Views/WheelModel.swift @@ -8,15 +8,11 @@ import UIKit -public enum GraphSize: String, Codable { - case small, medium, large -} - public enum GraphStyle: String, Codable { case unlimited, safetyMode } -public class WheelModel: MoleculeModelProtocol { +public class WheelModel: GraphSizeBase, MoleculeModelProtocol { public static var identifier: String = "wheel" public var id: String = UUID().uuidString @@ -27,11 +23,6 @@ public class WheelModel: MoleculeModelProtocol { } } - public var size: GraphSize = .small { - didSet { - updateSize() - } - } public var diameter: CGFloat = 24 public var lineWidth: CGFloat = 5 public var clockwise: Bool = true @@ -39,7 +30,8 @@ public class WheelModel: MoleculeModelProtocol { public var colors = [Color]() public var backgroundColor: Color? - public init() { + public override init() { + super.init() updateStyle() updateSize() } @@ -58,6 +50,7 @@ public class WheelModel: MoleculeModelProtocol { } required public init(from decoder: Decoder) throws { + super.init() let typeContainer = try decoder.container(keyedBy: CodingKeys.self) id = try typeContainer.decodeIfPresent(String.self, forKey: .id) ?? UUID().uuidString @@ -123,7 +116,7 @@ public class WheelModel: MoleculeModelProtocol { } } - func updateSize() { + public override func updateSize() { switch size { case .small: diameter = MFSizeObject(standardSize: 20)?.getValueBasedOnApplicationWidth() ?? 20