Add support for circular progress bar part 2
This commit is contained in:
parent
7fec6f540e
commit
1b0197ed2c
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user