iOS rounding UIView corners with draw enabled - ios

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.

Related

dashed border not working for UITableview cell's child view(dashed border width and hight not same as the view)

Here you can see the view which is inside the TableViewell:
Here is the code for adding dashed border:
func addDashedBorder()
{
let color = UIColor(displayP3Red: 178.0/255.0, green: 181.0/255.0, blue: 200.0/255.0, alpha: 1.0).cgColor
let shapeLayer:CAShapeLayer = CAShapeLayer()
let shapeRect = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
shapeLayer.bounds = shapeRect
shapeLayer.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2)
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = color
shapeLayer.lineWidth = 2
shapeLayer.lineJoin = CAShapeLayerLineJoin.round
shapeLayer.lineDashPattern = [6,3]
shapeLayer.path = UIBezierPath(roundedRect: shapeRect, cornerRadius: 4).cgPath
self.layer.addSublayer(shapeLayer)
}
I'm calling this function inside the awake from nib like this:
view.addDashedBorder().
Much easier approach...
Subclass UIView and put the border-related code inside it:
class DashedView: UIView {
let shapeLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
let color = UIColor(displayP3Red: 178.0/255.0, green: 181.0/255.0, blue: 200.0/255.0, alpha: 1.0).cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = color
shapeLayer.lineWidth = 2
shapeLayer.lineJoin = CAShapeLayerLineJoin.round
shapeLayer.lineDashPattern = [6,3]
layer.addSublayer(shapeLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
shapeLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 4).cgPath
}
}
Now you don't need any sort of view.addDashedBorder() calls.
Reason for issue: You are calling your border adding code from awakeFromNib.
Awake from is called when nib loaded, so that time your cell will not have the exact size for all subviews, when awakeFromNib gets called, all views have size as same as nib design. so awake from nib is the wrong place when you are setting or updating something which is related to size of any view.
Solution: The right place to do this is func layoutSubviews()
layoutSubviews() gets called when the size of any subview will be changed or the frames of views/subviews gets updated first time.
This is the right place to add border or your view, and make sure it will only one call of adding border because layoutSubviews will get multiple calls if the frame of any subview will change.**
Reference:
https://developer.apple.com/documentation/uikit/uiview/1622482-layoutsubviews

How to draw PieChart, Obtained Marks fill with some colour out of Total Marks without 3rd party library iOS Swift

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

Adding shadow layer with rounded corners on custom UIButton subclass

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:

How to call function from custom UIView class?

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()

create an Arc in swift for Ui collection view

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)

Resources