How to Make Rounded Corner Progress Bar in Swift

How to make rounded corner progress bar in swift?

Although you have set the corner radius, you also need to tell the view not to draw anything outside of the view's bounds by setting

self.progressView.clipsToBounds = true

Making UIProgressView Rounded corners

After searching and trying I decided to create my own custom progress view. Here is the code for anyone who may find them selevs in same problem.

import Foundation
import UIKit

class CustomHorizontalProgressView: UIView {
var progress: CGFloat = 0.0 {

didSet {
setProgress()
}
}

override init(frame: CGRect) {
super.init(frame: frame)

setup()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)

setup()
}

func setup() {
self.backgroundColor = UIColor.clearColor()
}

override func drawRect(rect: CGRect) {
super.drawRect(rect)

setProgress()
}

func setProgress() {
var progress = self.progress
progress = progress > 1.0 ? progress / 100 : progress

self.layer.cornerRadius = CGRectGetHeight(self.frame) / 2.0
self.layer.borderColor = UIColor.grayColor().CGColor
self.layer.borderWidth = 1.0

let margin: CGFloat = 6.0
var width = (CGRectGetWidth(self.frame) - margin) * progress
let height = CGRectGetHeight(self.frame) - margin

if (width < height) {
width = height
}

let pathRef = UIBezierPath(roundedRect: CGRect(x: margin / 2.0, y: margin / 2.0, width: width, height: height), cornerRadius: height / 2.0)

UIColor.redColor().setFill()
pathRef.fill()

UIColor.clearColor().setStroke()
pathRef.stroke()

pathRef.closePath()

self.setNeedsDisplay()
}
}

Just put above code in a swift file and drag drop a UIView in IB and give it class CustomHorizontalProgressView. and That is it.

How to make a rounded or filled and rounded progress view swift in swift (with CAShapeLayers)

It was not simple for me to find out how to make a progress bar rounded.

So I created a reusable code, so everybody can use it simple. I created a class.

Here is the class:

import UIKit

class CircularProgressView: UIView {


fileprivate var progressLayer = CAShapeLayer()
fileprivate var trackLayer = CAShapeLayer()
fileprivate var didConfigureLabel = false
fileprivate var rounded: Bool
fileprivate var filled: Bool


fileprivate let lineWidth: CGFloat?



var timeToFill = 3.43



var progressColor = UIColor.white {
didSet{
progressLayer.strokeColor = progressColor.cgColor
}
}

var trackColor = UIColor.white {
didSet{
trackLayer.strokeColor = trackColor.cgColor
}
}


var progress: Float {
didSet{
var pathMoved = progress - oldValue
if pathMoved < 0{
pathMoved = 0 - pathMoved
}

setProgress(duration: timeToFill * Double(pathMoved), to: progress)
}
}




fileprivate func createProgressView(){

self.backgroundColor = .clear
self.layer.cornerRadius = frame.size.width / 2
let circularPath = UIBezierPath(arcCenter: center, radius: frame.width / 2, startAngle: CGFloat(-0.5 * .pi), endAngle: CGFloat(1.5 * .pi), clockwise: true)
trackLayer.fillColor = UIColor.blue.cgColor


trackLayer.path = circularPath.cgPath
trackLayer.fillColor = .none
trackLayer.strokeColor = trackColor.cgColor
if filled {
trackLayer.lineCap = .butt
trackLayer.lineWidth = frame.width
}else{
trackLayer.lineWidth = lineWidth!
}
trackLayer.strokeEnd = 1
layer.addSublayer(trackLayer)

progressLayer.path = circularPath.cgPath
progressLayer.fillColor = .none
progressLayer.strokeColor = progressColor.cgColor
if filled {
progressLayer.lineCap = .butt
progressLayer.lineWidth = frame.width
}else{
progressLayer.lineWidth = lineWidth!
}
progressLayer.strokeEnd = 0
if rounded{
progressLayer.lineCap = .round
}


layer.addSublayer(progressLayer)

}





func trackColorToProgressColor() -> Void{
trackColor = progressColor
trackColor = UIColor(red: progressColor.cgColor.components![0], green: progressColor.cgColor.components![1], blue: progressColor.cgColor.components![2], alpha: 0.2)
}



func setProgress(duration: TimeInterval = 3, to newProgress: Float) -> Void{
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = duration

animation.fromValue = progressLayer.strokeEnd
animation.toValue = newProgress

progressLayer.strokeEnd = CGFloat(newProgress)

progressLayer.add(animation, forKey: "animationProgress")

}



override init(frame: CGRect){
progress = 0
rounded = true
filled = false
lineWidth = 15
super.init(frame: frame)
filled = false
createProgressView()
}

required init?(coder: NSCoder) {
progress = 0
rounded = true
filled = false
lineWidth = 15
super.init(coder: coder)
createProgressView()

}


init(frame: CGRect, lineWidth: CGFloat?, rounded: Bool) {


progress = 0

if lineWidth == nil{
self.filled = true
self.rounded = false
}else{
if rounded{
self.rounded = true
}else{
self.rounded = false
}
self.filled = false
}
self.lineWidth = lineWidth

super.init(frame: frame)
createProgressView()

}

}

And here is how to use:

Create a new progress view

let progressView = CircularProgressView(frame: CGRect(x: 0, y: 0, width: 100, height: 100), lineWidth: 15, rounded: false)

If you use nil for the line width, a filled circle will appear like the second picture above.

Set the progress- and the track color:

progressView.progressColor = .blue

progressView.trackColor = .lightGray

If you use progressView.trackColorToProgressColor() set a track color to the progress color, but with a less alpha.

Set the position of the progressView (example):

progressView.center = view.center

You can use this because the progressView is of type UIView.

Add the progressView as a subview of the ViewControllers view:

view.addSubview(progressView)

Set the progress of the progressView:

progressView.progress = 0.6

The progress will be animated. If you don't want to animate the progress you can set the progressView.timeToFill to 0. If you want a faster or lower animating you can use this method, too.

I hope this code was helpful.

Rounded corner throughout the whole progress

I think you should search into the sublayer to find de blue one and apply the same treatment

Creating a progress indicator with a rounded rectangle

I found a solution so the loading indicator works for round corners:

Sample Image

let queueShapeLayer = CAShapeLayer()

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

// Queue timer
let radius = addToQueue.layer.cornerRadius
let diameter = radius * 2
let totalLength = (addToQueue.frame.width - diameter) * 2 + (CGFloat.pi * diameter)

let queuePath = UIBezierPath(roundedRect: addToQueue.frame, cornerRadius: radius)
queueShapeLayer.path = queuePath.cgPath
queueShapeLayer.lineWidth = 5
queueShapeLayer.strokeColor = UIColor.white.cgColor
queueShapeLayer.fillColor = UIColor.clear.cgColor
queueShapeLayer.strokeStart = 0.25 - CGFloat.pi * diameter / 3 / totalLength // Change the '0.25' to 0.5, 0.75 etc. wherever you want the bar to start
queueShapeLayer.strokeEnd = queueShapeLayer.strokeStart + 0.5 // Change this to the value you want it to go to (in this case 0.5 or 50% loaded)
view.layer.addSublayer(queueShapeLayer)
}

After I had did this though, I was having problems that I couldn't animate the whole way round. To get around this, I created a second animation (setting strokeStart to 0) and then I placed completion blocks so I could trigger the animations at the correct time.

Tip:

Add animation.fillMode = CAMediaTimingFillMode.forwards & animation.isRemovedOnCompletion = false when using a CABasicAnimation for the animation to wait until you remove it.

I hope this formula helps anyone in the future!

If you need help, you can always message me and I am willing to help. :)

Gradient progress bar with rounded corners SpriteKit Swift

It's about SKCropNode + its maskNode property. From the docs:

SKCropNode is a container node that you use to crop other nodes in the
scene. You add other nodes to a crop node and set the crop node's
maskNode property. For example, here are some ways you might specify a
mask:

An untextured sprite that limits content to a rectangular portion of
the scene.

A textured sprite that works as a precise per-pixel mask.

A collection of child nodes that form a unique shape.

You can animate the shape or contents of the mask to implement
interesting effects such as hiding or revealing.

So, a simple example would be like this:

    class GameScene: SKScene {

override func sceneDidLoad() {

super.sceneDidLoad()
createProgressBar()
}

private func createProgressBar(){

let barFrame = CGRect(x: 0, y: 0, width: 300, height: 15)

if let cgImage = createImage(frame: barFrame) {

let texture = SKTexture(cgImage: cgImage)

let sprite = SKSpriteNode(texture: texture)
let cropNode = SKCropNode()
let mask = SKSpriteNode(color: .gray, size: barFrame.size)

cropNode.addChild(sprite)
cropNode.maskNode = mask

sprite.anchorPoint = CGPoint(x: 0.0, y: 0.5)
mask.anchorPoint = CGPoint(x: 0.0, y: 0.5)

var counter:Double = 0
let action = SKAction.run {[weak self, sprite] in
guard let `self` = self, counter < 100 else {
sprite?.removeAction(forKey: "loop")
return

}

counter += 1
let newWidth = self.getWidth(percents: counter, spriteWidth: barFrame.width)
print("Bar width \(newWidth), percentage \(counter)")

mask.size = CGSize(width: newWidth, height: barFrame.height)
}
let wait = SKAction.wait(forDuration: 0.05)
let sequence = SKAction.sequence([wait, action])

let loop = SKAction.repeatForever(sequence)



addChild(cropNode)
cropNode.position = CGPoint(x: self.frame.width / 2.0, y: self.frame.height / 2.0)

sprite.run(loop, withKey: "loop")


}
}

private func getWidth(percents:Double, spriteWidth:Double)->Double{

let onePercent = spriteWidth / 100.0

return onePercent * percents
}


private func createImage(frame barFrame:CGRect) -> CGImage?{

if let ciFilter = CIFilter(name: "CILinearGradient"){

let ciContext = CIContext()

ciFilter.setDefaults()

let startColor = CIColor(red: 0.75, green: 0.35, blue: 0.45, alpha: 1)
let endColor = CIColor(red: 0.45, green: 0.35, blue: 0.75, alpha: 1)

let startVector = CIVector(x: 0, y: 0)
let endVector = CIVector(x: barFrame.width, y: 0)

ciFilter.setValue(startColor, forKey: "inputColor0")
ciFilter.setValue(endColor, forKey: "inputColor1")
ciFilter.setValue(startVector, forKey: "inputPoint0")
ciFilter.setValue(endVector, forKey: "inputPoint1")

if let outputImage = ciFilter.outputImage {
let cgImage = ciContext.createCGImage(outputImage, from: CGRect(x: 0, y: 0, width: barFrame.width, height: barFrame.height))
return cgImage

}
}

return nil


}
}

Now cause this is just an example I won't go all the way to implement this right, but You can maybe make a class of it with designable and inspectable properties, optimize code, make it reusable etc. But the general idea is shown here.

You use SKCropNode to add progress bar in it, and use maskNode property to reveal progress bar as percentage increases. Also I gave a method to create texture programatically, but You can use just a .png file instead.

Crop node is here used only cause of a gradient (cause we don't wan't to scale image, but rather to show it part by part). Obviously, crop node is not needed if a progress bar had only one color.

Here is final result:

progress-bar

How to create non rounded corner UIProgressView in iOS

Simply change the progressViewStyle to UIProgressView.Style.bar, Default is UIProgressView.Style.default.

Swift 3.0 and above

self.progressViewStyle = .bar /* default is .default */

Swift 2.0

self.progressViewStyle = UIProgressViewStyle.Bar


Related Topics



Leave a reply



Submit