From 1b06d7007b4149600794f852a3577693babd7cef Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Mon, 13 Jan 2020 10:20:11 -0500 Subject: [PATCH] circle progress --- MVMCoreUI.xcodeproj/project.pbxproj | 4 + .../Atoms/Views/CircleProgressModel.swift | 128 +++++++++++++++++ MVMCoreUI/Atoms/Views/GraphView.swift | 132 +++--------------- 3 files changed, 149 insertions(+), 115 deletions(-) create mode 100644 MVMCoreUI/Atoms/Views/CircleProgressModel.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index b34f888b..b7c3e2fc 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -124,6 +124,7 @@ D28A837D23CCA86A00DFE4FC /* TabsListItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A837C23CCA86A00DFE4FC /* TabsListItemModel.swift */; }; D28A837F23CCA96400DFE4FC /* TabsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A837E23CCA96400DFE4FC /* TabsModel.swift */; }; D28A838123CCB0D800DFE4FC /* AccordionListItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A838023CCB0D800DFE4FC /* AccordionListItemModel.swift */; }; + D28A838323CCBD3F00DFE4FC /* CircleProgressModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28A838223CCBD3F00DFE4FC /* CircleProgressModel.swift */; }; D296E14722A5984C0051EBE7 /* MVMCoreUIViewConstrainingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; D29770C821F7C4AE00B2F0D0 /* TopLabelsView.m in Sources */ = {isa = PBXBuildFile; fileRef = D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */; }; D29770C921F7C4AE00B2F0D0 /* TopLabelsView.h in Headers */ = {isa = PBXBuildFile; fileRef = D29770C721F7C4AE00B2F0D0 /* TopLabelsView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -383,6 +384,7 @@ D28A837C23CCA86A00DFE4FC /* TabsListItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsListItemModel.swift; sourceTree = ""; }; D28A837E23CCA96400DFE4FC /* TabsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsModel.swift; sourceTree = ""; }; D28A838023CCB0D800DFE4FC /* AccordionListItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccordionListItemModel.swift; sourceTree = ""; }; + D28A838223CCBD3F00DFE4FC /* CircleProgressModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleProgressModel.swift; sourceTree = ""; }; D296E14622A597490051EBE7 /* MVMCoreUIViewConstrainingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIViewConstrainingProtocol.h; sourceTree = ""; }; D29770C621F7C4AE00B2F0D0 /* TopLabelsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TopLabelsView.m; sourceTree = ""; }; D29770C721F7C4AE00B2F0D0 /* TopLabelsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopLabelsView.h; sourceTree = ""; }; @@ -1040,6 +1042,7 @@ 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */, 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */, 01004F2F22721C3800991ECC /* RadioButton.swift */, + D28A838223CCBD3F00DFE4FC /* CircleProgressModel.swift */, 943784F3236B77BB006A1E82 /* GraphView.swift */, 943784F4236B77BB006A1E82 /* GraphViewAnimationHandler.swift */, ); @@ -1399,6 +1402,7 @@ 012A88EC238F084D00FE3DA1 /* FooterModel.swift in Sources */, D2A514672213885800345BFB /* StandardHeaderView.swift in Sources */, 01EB369023609801006832FA /* ListItemModel.swift in Sources */, + D28A838323CCBD3F00DFE4FC /* CircleProgressModel.swift in Sources */, D268C70C2386DFFD007F2C1C /* StackItemModel.swift in Sources */, DBEFFA04225A829700230692 /* Label.swift in Sources */, D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */, diff --git a/MVMCoreUI/Atoms/Views/CircleProgressModel.swift b/MVMCoreUI/Atoms/Views/CircleProgressModel.swift new file mode 100644 index 00000000..20f49dc0 --- /dev/null +++ b/MVMCoreUI/Atoms/Views/CircleProgressModel.swift @@ -0,0 +1,128 @@ +// +// CircleProgressModel.swift +// MVMCoreUI +// +// Created by Scott Pfeil on 1/13/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import UIKit + +public enum GraphSize: String, Codable { + case small, medium, large +} + +public enum GraphStyle: String, Codable { + case unlimited, safetyMode +} + +public class CircleProgressModel: MoleculeProtocol { + public static var identifier: String = "circleProgress" + public var style: GraphStyle = .unlimited { + didSet { + updateStyle() + } + } + + public var size: GraphSize = .small { + didSet { + updateSize() + } + } + public var diameter: CGFloat = 24 + public var lineWidth: CGFloat = 5 + public var clockwise: Bool = true + public var duration : Double = 1.0 + public var colors = [Color]() + public var backgroundColor: Color? + + public init() {} + + enum CircleProgressCodingKeys: String, CodingKey { + case style + case size + case diameter + case lineWidth + case clockwise + case duration + case colors + case backgroundColor + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CircleProgressCodingKeys.self) + if let style = try typeContainer.decodeIfPresent(GraphStyle.self, forKey: .style) { + self.style = style + } + if let size = try typeContainer.decodeIfPresent(GraphSize.self, forKey: .size) { + self.size = size + } + 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 + } + if let clockwise = try typeContainer.decodeIfPresent(Bool.self, forKey: .clockwise) { + self.clockwise = clockwise + } + if let duration = try typeContainer.decodeIfPresent(Double.self, forKey: .duration) { + self.duration = duration + } + if let colors = try typeContainer.decodeIfPresent([Color].self, forKey: .colors) { + self.colors = colors + } + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CircleProgressCodingKeys.self) + try container.encode(style, forKey: .style) + try container.encode(size, forKey: .size) + try container.encode(diameter, forKey: .diameter) + try container.encode(lineWidth, forKey: .lineWidth) + try container.encode(clockwise, forKey: .clockwise) + try container.encode(duration, forKey: .duration) + try container.encode(colors, forKey: .colors) + 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 updateStyle() { + switch style { + case .unlimited: + duration = 1.0 + clockwise = true + //current style, only the end part shows darker look + colors = getCGColorsFromArray(["#007AB8","#007AB8","#033554"]) + break + case .safetyMode: + duration = 1.5 + clockwise = true + colors = getCGColorsFromArray(["#CC4D0F","#CC4D0F","AB0309"]) + break + } + } + + 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 + } + } +} diff --git a/MVMCoreUI/Atoms/Views/GraphView.swift b/MVMCoreUI/Atoms/Views/GraphView.swift index 40c353b2..146daecf 100644 --- a/MVMCoreUI/Atoms/Views/GraphView.swift +++ b/MVMCoreUI/Atoms/Views/GraphView.swift @@ -8,113 +8,13 @@ import UIKit - -enum GraphSize: String { - case small, medium, large -} - -enum GraphStyle: String { - case unlimited, safetyMode -} - -///Graph Object contains properties -public struct GraphObject { - - var style: GraphStyle { - didSet { - updateStyle() - } - } - var size: GraphSize { - didSet { - updateSize() - } - } - var diameter: CGFloat = 24 - var lineWidth: CGFloat = 5 - var clockwise: Bool = true - var duration : Double = 1.0 - var colors = [CGColor]() - - public init(_ json: [AnyHashable : Any]?) { - style = .unlimited - size = .small - guard let json = json else { - return - } - if let styleString = json.optionalStringForKey("style") { - style = GraphStyle(rawValue: styleString) ?? .unlimited - } - if let sizeString = json.optionalStringForKey("size") { - size = GraphSize(rawValue: sizeString) ?? .small - } - updateStyle() - updateSize() - if let diameter = json.optionalCGFloatForKey("diameter") { - self.diameter = diameter - } - if let lineWidth = json.optionalCGFloatForKey("lineWidth") { - self.lineWidth = lineWidth - } - if let clockwise = json.optionalBoolForKey("clockwise") { - self.clockwise = clockwise - } - if let duration = json["duration"] as? Double { - self.duration = duration - } - if let colorArray = json.optionalArrayForKey("colors") as? [String] { - colors = getCGColorsFromArray(colorArray) - } - } - - func getCGColorsFromArray(_ colorArray: [String]) -> [CGColor] { - return colorArray.map { (colorString) -> CGColor in - return UIColor.mfGet(forHex: colorString).cgColor - } - } - - mutating func updateStyle() { - switch style { - case .unlimited: - duration = 1.0 - clockwise = true - //current style, only the end part shows darker look - colors = getCGColorsFromArray(["#007AB8","#007AB8","#033554"]) - break - case .safetyMode: - duration = 1.5 - clockwise = true - colors = getCGColorsFromArray(["#CC4D0F","#CC4D0F","AB0309"]) - break - } - } - - //those are - mutating 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 - } - } -} - - @objcMembers open class GraphView: View, MVMCoreUIViewConstrainingProtocol { var heightConstraint: NSLayoutConstraint? var gradientLayer: CALayer? - var graphObject: GraphObject? - + var graphModel: CircleProgressModel? { + return model as? CircleProgressModel + } // MARK: setup open override func setupView() { @@ -128,14 +28,16 @@ public struct GraphObject { override open func setWithModel(_ model: MoleculeProtocol?, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { super.setWithModel(model, delegateObject, additionalData) + guard let model = model as? CircleProgressModel else { return } + createGraphCircle(model) + rotationAnimation(model) } override open func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - let object = GraphObject(json) - graphObject = object - createGraphCircle(object) - rotationAnimation(object) + guard let graphModel = graphModel else { return } + createGraphCircle(graphModel) + rotationAnimation(graphModel) } class func getAngle(_ piValue: Double) -> Double { @@ -147,7 +49,7 @@ public struct GraphObject { } // MARK: circle - open func createGraphCircle(_ graphObject: GraphObject) { + open func createGraphCircle(_ graphObject: CircleProgressModel) { if let sublayers = layer.sublayers { for sublayer in sublayers { sublayer.removeAllAnimations() @@ -188,14 +90,14 @@ public struct GraphObject { | | | ------------- */ - func createGradientLayer(_ graphObject: GraphObject) -> CALayer { + func createGradientLayer(_ graphObject: CircleProgressModel) -> CALayer { let containLayer = CALayer() containLayer.frame = CGRect(x: 0, y: 0, width: graphObject.diameter, height: graphObject.diameter) let radius = graphObject.diameter / 2.0 //create graident layers guard graphObject.colors.count > 1 else { - containLayer.backgroundColor = graphObject.colors.first + containLayer.backgroundColor = graphObject.colors.first?.uiColor.cgColor return containLayer } var topGradientHeight : CGFloat = 0.0 @@ -214,7 +116,7 @@ public struct GraphObject { leftColors.removeLast() topLayer.colors = [leftColors.last!, rightColors.first!] } else { - topLayer.backgroundColor = leftColors.last + topLayer.backgroundColor = leftColors.last?.uiColor.cgColor } containLayer.addSublayer(topLayer) @@ -227,7 +129,7 @@ public struct GraphObject { if leftColors.count > 1 { leftLayer.colors = Array(leftColors) } else { - leftLayer.backgroundColor = leftColors.first + leftLayer.backgroundColor = leftColors.first?.uiColor.cgColor } containLayer.addSublayer(leftLayer) @@ -238,7 +140,7 @@ public struct GraphObject { if rightColors.count > 1 { rightLayer.colors = Array(rightColors) } else { - rightLayer.backgroundColor = rightColors.first + rightLayer.backgroundColor = rightColors.first?.uiColor.cgColor } containLayer.addSublayer(rightLayer) @@ -250,7 +152,7 @@ public struct GraphObject { } //MARK: Animation - func rotationAnimation(_ object: GraphObject) { + func rotationAnimation(_ object: CircleProgressModel) { MVMCoreDispatchUtility.performBlock(onMainThread:{ let rotation = CABasicAnimation(keyPath: "transform.rotation") let animationHandler = GraphViewAnimationHandler.shared @@ -281,7 +183,7 @@ public struct GraphObject { extension GraphView: CAAnimationDelegate { public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { - if let object = graphObject { + if let object = graphModel { rotationAnimation(object) } }