I have UIView in my first view controller. I have set that UIView class to Custom Progress View using following code.
class ProgressView: UIView {
let trackLayer = CAShapeLayer()
let shapeLayer = CAShapeLayer()
static let instance = ProgressView()
override func awakeFromNib() {
super.awakeFromNib()
let center = CGPoint.init(x: frame.width / 2, y: frame.height / 2)
let circularPath = UIBezierPath(arcCenter: center, radius: 30, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
trackLayer.path = circularPath.cgPath
trackLayer.strokeColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)
trackLayer.lineWidth = 3
trackLayer.fillColor = #colorLiteral(red: 0.5568627715, green: 0.3529411852, blue: 0.9686274529, alpha: 1)
trackLayer.lineCap = kCALineCapRound
self.layer.addSublayer(trackLayer)
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1)
shapeLayer.lineWidth = 3
shapeLayer.fillColor = #colorLiteral(red: 0.3794638515, green: 0.5145698786, blue: 0.9605538249, alpha: 1)
shapeLayer.lineCap = kCALineCapRound
shapeLayer.strokeEnd = 0
self.layer.addSublayer(shapeLayer)
}
}
There is also a function inside that custom UIView class which is for starting animation for filling circle with following code
func startProgress() {
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = 1
basicAnimation.duration = 60
basicAnimation.fillMode = kCAFillModeForwards
basicAnimation.isRemovedOnCompletion = false
shapeLayer.add(basicAnimation, forKey: "soBasic")
}
I need to call that startProgress function inside my First View controller. In viewDidLoad I have called ProgressView.instance.startProgress() but it is not firing. How can I call start progress function from another class?
In ProgressView you need to create an initialise or you can use default one.
class ProgressView: UIView {
//you can set any additional properties in it.
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
How to use
In view controller where you wanna use...
let pgView = ProgressView() //or any other initialise if you have created
.
.
.
pgView.startProgress()
Related
I want like this
Any code example will be appreciated
Here is a starting point ... created a circular progress view for your help
import UIKit
public class CircularProgressView: UIView {
// First create two layer properties
private lazy var circleLayer : CAShapeLayer = {
let shape = CAShapeLayer()
shape.fillColor = UIColor.clear.cgColor
shape.lineCap = .round
shape.lineWidth = 30.0
shape.strokeColor = #colorLiteral(red: 0.7999292612, green: 0.8000453115, blue: 0.7999040484, alpha: 1)
return shape
}()
private lazy var progressLayer : CAShapeLayer = {
let progress = CAShapeLayer()
progress.fillColor = UIColor.clear.cgColor
// progress.lineCap = .round
progress.lineWidth = 30.0
progress.strokeEnd = 0
progress.strokeColor = #colorLiteral(red: 0.07347138971, green: 0.5590900779, blue: 0.8216868043, alpha: 1)
return progress
}()
override init(frame: CGRect) {
super.init(frame: frame)
createCircularPath()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
createCircularPath()
}
private func createCircularPath() {
updatePath()
layer.addSublayer(circleLayer)
layer.addSublayer(progressLayer)
}
public override func layoutSubviews() {
updatePath()
}
private func updatePath() {
let circularPath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: 80, startAngle: -.pi / 2, endAngle: 3 * .pi / 2, clockwise: true)
circleLayer.path = circularPath.cgPath
progressLayer.path = circularPath.cgPath
}
}
public extension CircularProgressView {
func progressAnimation(_ percentage:Float) {
let circularProgressAnimation = CABasicAnimation(keyPath: "strokeEnd")
circularProgressAnimation.duration = 1
circularProgressAnimation.toValue = Float( percentage / 100 )
circularProgressAnimation.fillMode = .forwards
circularProgressAnimation.isRemovedOnCompletion = false
progressLayer.add(circularProgressAnimation, forKey: "progressAnim")
}
}
What you actually want is something custom. This can be built custom or you can use a thridparty library with certain modifications. You a bunch of them on cocoapods but the one i would suggest is
https://github.com/danielgindi/Charts
I'm trying to apply a shadow and rounded corners on custom UIButton subclass to use in on the storyboard
Here is my code :
import UIKit
class customButton: UIButton {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
setGradientBackground()
setShadowLayer()
}
private func setup() {
clipsToBounds = true
layer.cornerRadius = 8
}
private func setGradientBackground() {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [
UIColor(red: 0.18, green: 0.5, blue: 0.93, alpha: 1).cgColor,
UIColor(red: 0.18, green: 0.61, blue: 0.86, alpha: 1).cgColor
]
gradientLayer.locations = [0,1]
gradientLayer.startPoint = CGPoint(x: 0.25, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
layer.insertSublayer(gradientLayer, at: 0)
}
private func setShadowLayer(){
let subLayer = CALayer()
subLayer.frame = bounds
subLayer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 8).cgPath
subLayer.shadowColor = UIColor.red.cgColor
subLayer.shadowOpacity = 1
subLayer.shadowRadius = 12
subLayer.shadowOffset = CGSize(width: 0, height: 2)
layer.insertSublayer(subLayer, at: 0)
}
}
When I'm setting the clipsToBounds to true it remove the shadow, so I decided to add a new sublayer func setShadowLayer() and nothing worked so far.
Try also setting the .cornerRadius on the gradient layer:
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
// add this line
gradientLayer.cornerRadius = 8
layer.insertSublayer(gradientLayer, at: 0)
My result:
I use this class to create circular progress:
class ProgressBarView: UIView {
var bgPath: UIBezierPath!
var shapeLayer: CAShapeLayer!
var progressLayer: CAShapeLayer!
var progress: Float = 0 {
willSet(newValue)
{
progressLayer.strokeEnd = CGFloat(newValue)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
bgPath = UIBezierPath()
self.simpleShape()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
bgPath = UIBezierPath()
self.simpleShape()
}
func simpleShape()
{
createCirclePath()
shapeLayer = CAShapeLayer()
shapeLayer.path = bgPath.cgPath
shapeLayer.lineWidth = 2.5
shapeLayer.fillColor = nil
shapeLayer.strokeColor = UIColor.black.cgColor.copy(alpha: 0.2)
progressLayer = CAShapeLayer()
progressLayer.path = bgPath.cgPath
progressLayer.lineCap = kCALineCapRound
progressLayer.lineWidth = 2.5
progressLayer.fillColor = nil
progressLayer.strokeColor = UIColor(red: 0/255, green: 122/255, blue: 255/255, alpha: 1.0).cgColor
progressLayer.strokeEnd = 0.0
self.layer.addSublayer(shapeLayer)
self.layer.addSublayer(progressLayer)
}
private func createCirclePath()
{
let x = self.frame.width/2
let y = self.frame.height/2
let center = CGPoint(x: x, y: y)
print(x,y,center)
bgPath.addArc(withCenter: center, radius: x, startAngle: CGFloat(11), endAngle: CGFloat(17.28), clockwise: true)
bgPath.close()
}
}
And in my ViewController I use this class for my progressView. I want to change size of my progressView for iPhone SE. I trying create width constraint in storyboard and change size of progressView programmatically like:
if self.view.frame.height == 568 {
progressViewWidthConstraint.constant = 100
}
But it doesn't work. How to change size for iPhone SE?
Update
You need to re-layout
progressViewWidthConstraint.constant = 100
self.view.layoutIfNeeded()
Hi i like to create the following view in ios swift for adding in UIcollection view , how can i achieve this ?
my current code to draw which is returning circle view
#IBInspectable public var fillColor: UIColor = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1) { didSet { setNeedsLayout() } }
#IBInspectable public var strokeColor: UIColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1) { didSet { setNeedsLayout() } }
#IBInspectable public var lineWidth: CGFloat = 0 { didSet { setNeedsLayout() } }
lazy private var shapeLayer: CAShapeLayer = {
let _shapeLayer = CAShapeLayer()
self.layer.insertSublayer(_shapeLayer, at: 0)
return _shapeLayer
}()
override func layoutSubviews() {
super.layoutSubviews()
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let radius = (min(bounds.size.width, bounds.size.height) - lineWidth) / 2
shapeLayer.path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: 90, clockwise: true).cgPath
shapeLayer.fillColor = fillColor.cgColor
shapeLayer.strokeColor = strokeColor.cgColor
shapeLayer.lineWidth = lineWidth
}
You can draw the arc in view using BezierPath. Add this arcView in your collection view cell.
Let's see the ArcView :
class ArcView : UIView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
super.draw(rect)
createArc(rect: rect)
}
private func createArc(rect : CGRect) {
let center = CGPoint(x: rect.width/2, y: rect.height/2)
let lineWidth : CGFloat = 50.0
let radius = rect.width / 2 - lineWidth
let startingAngle = CGFloat(-10.0/180) * CGFloat.pi
let endingAngle = CGFloat(-80/180.0) * CGFloat.pi
let bezierPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: startingAngle , endAngle: endingAngle, clockwise: false)
bezierPath.lineWidth = lineWidth
UIColor(red: 249/255.0, green: 179/255.0, blue: 127/255.0, alpha: 1.0).setStroke()
bezierPath.stroke()
}
}
The center of arc is the middle of view.
Drawing the arc from -10 degree to -80 degree in anti-clockwise direction.
Adding the ArcView in ViewController's view Hierarchy (You will add this view in your collectionView Cell)
import UIKit
class ViewController: UIViewController {
private var arcView : ArcView!
override func viewDidLoad() {
super.viewDidLoad()
arcView = ArcView(frame: view.frame)
view.addSubview(arcView)
arcView.setNeedsDisplay()
}
}
Output :
The sample code is here
Try this and set angle as you want
let circlePath = UIBezierPath(arcCenter: CGPoint(x: 100,y: 100), radius: CGFloat(20), startAngle: CGFloat(0), endAngle:CGFloat(Double.pi / 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
//change the fill color
shapeLayer.fillColor = UIColor.clear.cgColor
//you can change the stroke color
shapeLayer.strokeColor = UIColor.red.cgColor
//you can change the line width
shapeLayer.lineWidth = 3.0
view.layer.addSublayer(shapeLayer)
When draw method is commented out inside the below code the view shows up circular, but when it is uncommented out the view shows up as a rectangle again. I could comment out the body of draw to have it just be an empty method, but the view would still show up as a rectangle.
I've tried moving the cornerRadius line to the end of draw, beginning of draw, and end of drawInnerCircle but to no avail. I was wondering if there was still a way to make the view rounded with draw method enabled?
import UIKit
class IconView: UIView {
override init(frame:CGRect) {
super.init(frame : frame)
self.backgroundColor = UIColor(red: 47/255, green: 49/255, blue: 53/255, alpha: 1.0)
self.layer.cornerRadius = self.frame.size.height / 2.0
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
drawInnerCircle()
}
internal func drawInnerCircle() -> () {
let halfSize:CGFloat = min( self.frame.size.width/2, bounds.size.height/2)
let desiredLineWidth:CGFloat = 1 // your desired value
let circlePath = UIBezierPath(
arcCenter: CGPoint(x:halfSize,y:halfSize),
radius: CGFloat( halfSize - 3 ),
startAngle: CGFloat(0),
endAngle:CGFloat(M_PI * 2),
clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor(red: 96/255, green: 99/255, blue: 105/255, alpha: 1.0).cgColor
shapeLayer.lineWidth = desiredLineWidth
layer.addSublayer(shapeLayer)
}
}
Add the following:
self.layer.masksToBounds = true
below the line:
self.layer.cornerRadius = self.frame.size.height / 2.0
By the way there are some problem described by the above comments.