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 {
|
@objcMembers open class CircularProgressBar: View, MVMCoreUIViewConstrainingProtocol {
|
||||||
|
|
||||||
var heightConstraint: NSLayoutConstraint?
|
var heightConstraint: NSLayoutConstraint?
|
||||||
weak var gradientLayer: CALayer?
|
|
||||||
var graphModel: CircularProgressBarModel? {
|
var graphModel: CircularProgressBarModel? {
|
||||||
return model as? 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
|
// MARK: setup
|
||||||
open override func setupView() {
|
open override func setupView() {
|
||||||
super.setupView()
|
super.setupView()
|
||||||
@ -27,46 +98,21 @@ import UIKit
|
|||||||
override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
override open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) {
|
||||||
super.set(with: model, delegateObject, additionalData)
|
super.set(with: model, delegateObject, additionalData)
|
||||||
guard let model = model as? CircularProgressBarModel else { return }
|
guard let model = model as? CircularProgressBarModel else { return }
|
||||||
createGraphCircle(model)
|
|
||||||
}
|
configureProgressViewToBeCircular()
|
||||||
|
|
||||||
class func getAngle(_ piValue: Double) -> Double {
|
if let color = model.color {
|
||||||
return piValue / (2.0 * Double.pi) * 360.0
|
setProgressColor = color.uiColor
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
if let backgroundColor = model.backgroundColor {
|
||||||
gradient.type = .conic
|
setTrackColor = backgroundColor.uiColor
|
||||||
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)
|
setProgressWithAnimation(duration: 0.5, value: Float(graphModel?.percent ?? 0) / 100)
|
||||||
let radius = (graphObject.diameter - graphObject.lineWidth) / 2.0
|
drawLabel()
|
||||||
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
|
//MARK: MVMCoreUIViewConstrainingProtocol
|
||||||
public func needsToBeConstrained() -> Bool {
|
public func needsToBeConstrained() -> Bool {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -83,16 +83,16 @@ public class CircularProgressBarModel: MoleculeModelProtocol {
|
|||||||
func updateSize() {
|
func updateSize() {
|
||||||
switch size {
|
switch size {
|
||||||
case .small:
|
case .small:
|
||||||
diameter = MFSizeObject(standardSize: 20)?.getValueBasedOnApplicationWidth() ?? 20
|
diameter = MFSizeObject(standardSize: 64)?.getValueBasedOnApplicationWidth() ?? 64
|
||||||
lineWidth = MFSizeObject(standardSize: 4)?.getValueBasedOnApplicationWidth() ?? 4
|
lineWidth = MFSizeObject(standardSize: 5)?.getValueBasedOnApplicationWidth() ?? 5
|
||||||
break
|
break
|
||||||
case .medium:
|
case .medium:
|
||||||
diameter = MFSizeObject(standardSize: 100)?.getValueBasedOnApplicationWidth() ?? 100
|
diameter = MFSizeObject(standardSize: 84)?.getValueBasedOnApplicationWidth() ?? 84
|
||||||
lineWidth = MFSizeObject(standardSize: 8)?.getValueBasedOnApplicationWidth() ?? 8
|
lineWidth = MFSizeObject(standardSize: 5)?.getValueBasedOnApplicationWidth() ?? 5
|
||||||
break
|
break
|
||||||
case .large:
|
case .large:
|
||||||
diameter = MFSizeObject(standardSize: 180)?.getValueBasedOnApplicationWidth() ?? 180
|
diameter = MFSizeObject(standardSize: 124)?.getValueBasedOnApplicationWidth() ?? 124
|
||||||
lineWidth = MFSizeObject(standardSize: 12)?.getValueBasedOnApplicationWidth() ?? 12
|
lineWidth = MFSizeObject(standardSize: 5)?.getValueBasedOnApplicationWidth() ?? 5
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,6 +67,7 @@ open class CoreUIModelMapping: ModelMapping {
|
|||||||
ModelRegistry.register(handler: LoadImageView.self, for: ImageViewModel.self)
|
ModelRegistry.register(handler: LoadImageView.self, for: ImageViewModel.self)
|
||||||
ModelRegistry.register(handler: Line.self, for: LineModel.self)
|
ModelRegistry.register(handler: Line.self, for: LineModel.self)
|
||||||
ModelRegistry.register(handler: Wheel.self, for: WheelModel.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: Toggle.self, for: ToggleModel.self)
|
||||||
ModelRegistry.register(handler: CheckboxLabel.self, for: CheckboxLabelModel.self)
|
ModelRegistry.register(handler: CheckboxLabel.self, for: CheckboxLabelModel.self)
|
||||||
ModelRegistry.register(handler: Arrow.self, for: ArrowModel.self)
|
ModelRegistry.register(handler: Arrow.self, for: ArrowModel.self)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user