I to create my own custom progressbar ie:NSProgressIndicator
using swift how do I do that?

- 380
- 3
- 15
2 Answers
You can just make your own progress indicator, e.g.:
@IBDesignable
open class ColorfulProgressIndicator: NSView {
@IBInspectable open var doubleValue: Double = 50 { didSet { needsLayout = true } }
@IBInspectable open var minValue: Double = 0 { didSet { needsLayout = true } }
@IBInspectable open var maxValue: Double = 100 { didSet { needsLayout = true } }
@IBInspectable open var backgroundColor: NSColor = .lightGray { didSet { layer?.backgroundColor = backgroundColor.cgColor } }
@IBInspectable open var progressColor: NSColor = .blue { didSet { progressShapeLayer.fillColor = progressColor.cgColor } }
@IBInspectable open var borderColor: NSColor = .clear { didSet { layer?.borderColor = borderColor.cgColor } }
@IBInspectable open var borderWidth: CGFloat = 0 { didSet { layer?.borderWidth = borderWidth } }
@IBInspectable open var cornerRadius: CGFloat = 0 { didSet { layer?.cornerRadius = cornerRadius } }
private lazy var progressShapeLayer: CAShapeLayer = {
let shapeLayer = CAShapeLayer()
shapeLayer.fillColor = progressColor.cgColor
return shapeLayer
}()
public override init(frame: NSRect = .zero) {
super.init(frame: frame)
configure()
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
// needed because IB doesn't don't honor `wantsLayer`
open override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
layer = CALayer()
configure()
}
open override func layout() {
super.layout()
updateProgress()
}
open func animate(to doubleValue: Double? = nil, minValue: Double? = nil, maxValue: Double? = nil, duration: TimeInterval = 0.25) {
let currentPath = progressShapeLayer.presentation()?.path ?? progressShapeLayer.path
// stop prior animation, if any
progressShapeLayer.removeAnimation(forKey: "updatePath")
// update progress properties
if let doubleValue = doubleValue { self.doubleValue = doubleValue }
if let minValue = minValue { self.minValue = minValue }
if let maxValue = maxValue { self.maxValue = maxValue }
// create new animation
let animation = CABasicAnimation(keyPath: "path")
animation.duration = duration
animation.fromValue = currentPath
animation.toValue = progressPath
progressShapeLayer.add(animation, forKey: "updatePath")
}
}
private extension ColorfulProgressIndicator {
func configure() {
wantsLayer = true
layer?.cornerRadius = cornerRadius
layer?.backgroundColor = backgroundColor.cgColor
layer?.borderWidth = borderWidth
layer?.borderColor = borderColor.cgColor
layer?.addSublayer(progressShapeLayer)
}
func updateProgress() {
progressShapeLayer.path = progressPath
}
var progressPath: CGPath? {
guard minValue != maxValue else { return nil }
let percent = max(0, min(1, CGFloat((doubleValue - minValue) / (maxValue - minValue))))
let rect = NSRect(origin: bounds.origin, size: CGSize(width: bounds.width * percent, height: bounds.height))
return CGPath(rect: rect, transform: nil)
}
}
You can either just set its doubleValue
, minValue
and maxValue
, or if you want to animate the change, just:
progressIndicator.animate(to: 75)
For example, below I set the progressColor
and borderColor
to .red
, set the borderWidth
to 1
, set the cornerRadius
to 10
. I then started animating to 75
, and then, before it’s even done, triggered another animation to 100
(to illustrate that animations can pick up from wherever it left off):
There are tons of ways of implementing this (so get too lost in the details of the implementation, above), but it illustrates that creating our own progress indicators is pretty easy.

- 415,655
- 72
- 787
- 1,044
-
ok yes this is exactly what i was looking for thank you so much. I'm new to this so i wasn't sure what terms to use. – Death Guard Feb 06 '20 at 05:54
You can subclass it just like any other view. But for all you're doing that is likely unnecessary.
class CustomIndicator: NSProgressIndicator {
// ...
}
As far as setting the height goes, you can do this by initializing the view with a custom frame.
let indicator = NSProgressIndicator(frame: NSRect(x: 0, y: 0, width: 100, height: 20))
There is a property called controlTint that you can set on NSProgressIndicator, but this only allows you to set the color to the predefined ones under NSControlTint. For truly custom colors, I'd recommend this route using Quartz filters via the Interface Builder.

- 61
- 5