Refactor codes for circular progress UI.

This commit is contained in:
Xi Zhang 2024-07-11 13:12:29 -04:00
parent 1b0197ed2c
commit 20d818b2c2
2 changed files with 71 additions and 42 deletions

View File

@ -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 {

View File

@ -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] {