I need to draw a view with UIBezierPath at the bottom of the view just like this image.
I try with this code but oval draw at the half of the image not at bottom of the view like in picture
Here is my output and code
override func draw(_ rect: CGRect) {
super.draw(rect)
let shapeLayer = CAShapeLayer(layer: imgCamera.layer)
shapeLayer.path = self.pathCurvedForView(givenView: imgCamera).cgPath
shapeLayer.frame = imgCamera.bounds
shapeLayer.masksToBounds = true
imgCamera.layer.mask = shapeLayer
}
private func pathCurvedForView(givenView: UIView) ->UIBezierPath
{
let ovalRect = givenView.frame
let ovalPath = UIBezierPath()
ovalPath.addArc(withCenter: CGPoint(x: ovalRect.midX, y: ovalRect.midY), radius: ovalRect.width / 2, startAngle: 0 * CGFloat.pi/180, endAngle: -180 * CGFloat.pi/180, clockwise: true)
ovalPath.addLine(to: CGPoint(x: ovalRect.midX, y: ovalRect.midY))
ovalPath.close()
UIColor.gray.setFill()
ovalPath.fill()
return ovalPath
}
Code Work:
imageView.layer.cornerRadius = imageView.frame.width/2
imageView.layer.borderWidth = 5
imageView.layer.borderColor = UIColor.white.cgColor
let gradientLayer = CAGradientLayer()
gradientLayer.frame = imageView.bounds
gradientLayer.colors = [UIColor.clear.cgColor, UIColor.clear.cgColor, UIColor.clear.cgColor, lightBlack.cgColor , lightBlack.cgColor]
gradientLayer.locations = [NSNumber(value: 0.0), NSNumber(value: 0.5), NSNumber(value: 0.75),NSNumber(value: 0.75), NSNumber(value: 1.0)]
imageView.layer.addSublayer(gradientLayer)
Output :
You can add the cam UIButton directly as subView instead and using the UIImageView maskToBounds to achieve that effect, adding border width and border color to the UIImageView and making the UIImageView touchable is enough
Example Code
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
imageView.isUserInteractionEnabled = true
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.imageView.layer.cornerRadius = imageView.frame.size.height/2
self.imageView.layer.borderColor = UIColor.white.cgColor
self.imageView.layer.borderWidth = 2
self.imageView.layer.masksToBounds = true
self.view.backgroundColor = UIColor.gray
let button = UIButton(type: .custom)
button.backgroundColor = UIColor.black.withAlphaComponent(0.3)
button.frame = CGRect(x: 0, y: self.imageView.frame.size.height - self.imageView.frame.size.height * 0.2, width: self.imageView.frame.size.width, height: self.imageView.frame.size.height * 0.2)
button.setImage(UIImage(named: "chatBubbleChat2Filled"), for: .normal)
self.imageView.addSubview(button)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
startAngle and endAngle are supposed to be values in radians. The angle calculation looks really weird in your case, what were you trying to achieve? To me, the desired output look like it needs an arc from 5/8 PI to 7/8 PI, counter-clockwise. In your case, it goes from 0 to ~-PI, clockwise.
Try
ovalPath.addArc(withCenter: CGPoint(x: ovalRect.midX, y: ovalRect.midY), radius: ovalRect.width / 2, startAngle: CGFloat.pi * 5.0/8.0, endAngle: CGFloat.pi * 7.0/8.0, clockwise: false)
ovalPath.close()
You don't need to add addLine by the way, close() will do that fot you
Related
How to draw about three circle in horizontally area with main and ring color in rectangle. I need to create custom button with this circles, something like this:
Is there any good way to do this?
We can design such kind of views with UIStackView in very ease manner.
Take a stackView, set its alignment to center, axis to horizontal and distribution to fill. Create a UILabel/UIButton/UIImageView or even UIView and add rounded radius and border to it. Finally, add those views to the main stackView.
Try this.
override func viewDidLoad() {
super.viewDidLoad()
//Setup stackView
let myStackView = UIStackView()
myStackView.axis = .horizontal
myStackView.alignment = .center
myStackView.distribution = .fillEqually
myStackView.spacing = 8
view.addSubview(myStackView)
//Setup circles
let circle_1 = circleLabel()
let circle_2 = circleLabel()
let circle_3 = circleLabel()
myStackView.addArrangedSubview(circle_1)
myStackView.addArrangedSubview(circle_2)
myStackView.addArrangedSubview(circle_3)
myStackView.translatesAutoresizingMaskIntoConstraints = false
myStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0.0).isActive = true
myStackView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0.0).isActive = true
}
func circleLabel() -> UILabel {
let label = UILabel()
label.backgroundColor = UIColor.red
label.layer.cornerRadius = 12.5
label.layer.masksToBounds = true
label.layer.borderColor = UIColor.orange.cgColor
label.layer.borderWidth = 3.0
label.widthAnchor.constraint(equalToConstant: 25.0).isActive = true
label.heightAnchor.constraint(equalToConstant: 25.0).isActive = true
return label
}
To make a Single Circle like that, you need to make use of UIBezierPath and CAShapeLayer .
let outerCirclePath = UIBezierPath(arcCenter: CGPoint(x: 100,y: 100), radius: CGFloat(50), startAngle: CGFloat(0), endAngle:CGFloat(Double.pi * 2), clockwise: true)
let outerCircleShapeLayer = CAShapeLayer()
outerCircleShapeLayer.path = outerCirclePath.cgPath
outerCircleShapeLayer.fillColor = UIColor.white.cgColor
outerCircleShapeLayer.lineWidth = 3.0
view.layer.addSublayer(outerCircleShapeLayer)
// Drawing the inner circle
let innerCirclePath = UIBezierPath(arcCenter: CGPoint(x: 100,y: 100), radius: CGFloat(40), startAngle: CGFloat(0), endAngle:CGFloat(Double.pi * 2), clockwise: true)
let innerCircleShapeLayer = CAShapeLayer()
innerCircleShapeLayer.path = innerCirclePath.cgPath
innerCircleShapeLayer.fillColor = UIColor.blue.cgColor
view.layer.addSublayer(innerCircleShapeLayer)
I have attached an image below for the Playground version of it .
Just play around with arcCenter and radius values and you will get the desired output
My team helped me and here is solution to create this with dynamically changing state of circles (with different stroke and fill colors):
import UIKit
#IBDesignable
class CirclesButton: UIControl {
#IBInspectable
var firstCircle: Bool = false {
didSet {
setNeedsDisplay()
}
}
#IBInspectable
var secondCircle: Bool = false {
didSet {
setNeedsDisplay()
}
}
#IBInspectable
var thirdCircle: Bool = false {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
// get context
guard let context = UIGraphicsGetCurrentContext() else { return }
// make configurations
context.setLineWidth(1.0);
context.setStrokeColor(UIColor.white.cgColor)
context.setFillColor(red: 0.0, green: 0.58, blue: 1.0, alpha: 1.0)
// find view center
let dotSize:CGFloat = 11.0
let viewCenter = CGPoint(x: rect.midX, y: rect.midY)
// find personal dot rect
var dotRect = CGRect(x: viewCenter.x - dotSize / 2.0, y: viewCenter.y - dotSize / 2.0, width: dotSize, height: dotSize)
if secondCircle {
context.fillEllipse(in: dotRect)
}
context.strokeEllipse(in: dotRect)
// find global notes rect
dotRect = CGRect(x: viewCenter.x - dotSize * 1.5 - 4.0, y: viewCenter.y - dotSize / 2.0, width: dotSize, height: dotSize)
if firstCircle {
context.fillEllipse(in: dotRect)
}
context.strokeEllipse(in: dotRect)
// find music rect
dotRect = CGRect(x: viewCenter.x + dotSize / 2.0 + 4.0, y: viewCenter.y - dotSize / 2.0, width: dotSize, height: dotSize)
if thirdCircle {
context.setFillColor(red: 0.0, green: 1.0, blue: 0.04, alpha: 1.0)
context.fillEllipse(in: dotRect)
}
context.strokeEllipse(in: dotRect)
}
}
It will looks like: CirclesButton
Сode:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let buttonSize: CGFloat = 80
let firstButton = CustomButton(position: CGPoint(x: 0, y: 0), size: buttonSize, color: .blue)
self.view.addSubview(firstButton)
let secondButton = CustomButton(position: CGPoint(x: firstButton.frame.maxX, y: 0), size: buttonSize, color: .blue)
self.view.addSubview(secondButton)
let thirdButton = CustomButton(position: CGPoint(x: secondButton.frame.maxX, y: 0), size: buttonSize, color: .green)
self.view.addSubview(thirdButton)
}
}
class CustomButton: UIButton {
init(position: CGPoint, size: CGFloat, color: UIColor) {
super.init(frame: CGRect(x: position.x, y: position.y, width: size, height: size))
self.backgroundColor = color
self.layer.cornerRadius = size / 2
self.clipsToBounds = true
self.layer.borderWidth = 4.0 // make it what ever you want
self.layer.borderColor = UIColor.white.cgColor
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
You can handle button tapped like:
override func viewDidLoad() {
super.viewDidLoad()
firstButton.addTarget(self, action: #selector(handleFirstButton), for: .touchUpInside)
}
#objc func handleFirstButton(sender: UIButton) {
print("first button tapped")
}
Best and Universal Solution for **Button or Label creation (Fully Dynamic)**
var x = 10
var y = 5
var buttonHeight = 40
var buttonWidth = 40
for i in 0..<3 {
let roundButton = UIButton(frame: CGRect(x: x, y: y, width: buttonWidth, height: buttonHeight))
roundButton.setTitle("Butt\(i)", for: .normal)
roundButton.layer.cornerRadius = roundButton.bounds.size.height/2
yourButtonBackView.addSubview(roundButton)
x = x + buttonWidth + 10
if x >= Int(yourButtonBackView.frame.width - 30) {
y = y + buttonHeight + 10
x = 10
}
}
Hi i am trying to write colour picker in swift that looks like this.
But so far I managed this.
Draw circle was easy, heres code...
fileprivate func setupScene(){
let circlePath: UIBezierPath = UIBezierPath(arcCenter: CGPoint(x: self.wheelView.frame.width/2, y: self.wheelView.frame.height/2), radius: CGFloat(self.wheelView.frame.height/2), startAngle: CGFloat(0), endAngle:CGFloat(Double.pi * 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
//color inside circle
shapeLayer.fillColor = UIColor.clear.cgColor
//colored border of circle
shapeLayer.strokeColor = UIColor.purple.cgColor
//width size of border
shapeLayer.lineWidth = 10
wheelView.layer.addSublayer(shapeLayer)
}
#IBOutlet var wheelView: UIView!
But now I don't know how to insert rainbow colours ... I tried CAGradientLayer but it was not visible. Any good advice?
Details
Xcode 9.1, swift 4
Xcode 10.2.1 (10E1001), Swift 5
Solution
The code was taken from https://github.com/joncardasis/ChromaColorPicker
import UIKit
class RainbowCircle: UIView {
private var radius: CGFloat {
return frame.width>frame.height ? frame.height/2 : frame.width/2
}
private var stroke: CGFloat = 10
private var padding: CGFloat = 5
//MARK: - Drawing
override func draw(_ rect: CGRect) {
super.draw(rect)
drawRainbowCircle(outerRadius: radius - padding, innerRadius: radius - stroke - padding, resolution: 1)
}
init(frame: CGRect, lineHeight: CGFloat) {
super.init(frame: frame)
stroke = lineHeight
}
required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
/*
Resolution should be between 0.1 and 1
*/
private func drawRainbowCircle(outerRadius: CGFloat, innerRadius: CGFloat, resolution: Float) {
guard let context = UIGraphicsGetCurrentContext() else { return }
context.saveGState()
context.translateBy(x: self.bounds.midX, y: self.bounds.midY) //Move context to center
let subdivisions:CGFloat = CGFloat(resolution * 512) //Max subdivisions of 512
let innerHeight = (CGFloat.pi*innerRadius)/subdivisions //height of the inner wall for each segment
let outterHeight = (CGFloat.pi*outerRadius)/subdivisions
let segment = UIBezierPath()
segment.move(to: CGPoint(x: innerRadius, y: -innerHeight/2))
segment.addLine(to: CGPoint(x: innerRadius, y: innerHeight/2))
segment.addLine(to: CGPoint(x: outerRadius, y: outterHeight/2))
segment.addLine(to: CGPoint(x: outerRadius, y: -outterHeight/2))
segment.close()
//Draw each segment and rotate around the center
for i in 0 ..< Int(ceil(subdivisions)) {
UIColor(hue: CGFloat(i)/subdivisions, saturation: 1, brightness: 1, alpha: 1).set()
segment.fill()
//let lineTailSpace = CGFloat.pi*2*outerRadius/subdivisions //The amount of space between the tails of each segment
let lineTailSpace = CGFloat.pi*2*outerRadius/subdivisions
segment.lineWidth = lineTailSpace //allows for seemless scaling
segment.stroke()
//Rotate to correct location
let rotate = CGAffineTransform(rotationAngle: -(CGFloat.pi*2/subdivisions)) //rotates each segment
segment.apply(rotate)
}
context.translateBy(x: -self.bounds.midX, y: -self.bounds.midY) //Move context back to original position
context.restoreGState()
}
}
Usage
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let rainbowCircle = RainbowCircle(frame: CGRect(x: 50, y: 50, width: 240, height: 420), lineHeight: 5)
rainbowCircle.backgroundColor = .clear
view.addSubview(rainbowCircle)
}
}
Result
I have literally looked over the web for like 2to3 hours, unfortunately couldn't find anything, (2nd problem: being new to swift ->this can be ignored)
Basically I need Circular dashed Loading bar : Every dot should animate Individually like a circular load..
I was able to make a circular dashed circle but couldn't animate it...
Any help would be appreciated even if thats an library from github or anything..
Here's my code for creating a circular dashed circle..
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
let circlePath = UIBezierPath(arcCenter: CGPoint(x: 100,y: 100), radius: 100.0, startAngle: 0.0, endAngle:CGFloat(M_PI * 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
shapeLayer.position = CGPoint(x: 100, y: 100)
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.lineWidth = 6.0
let one : NSNumber = 1
let two : NSNumber = 13
shapeLayer.lineDashPattern = [one,two]
shapeLayer.lineCap = kCALineCapRound
view.layer.addSublayer(shapeLayer)
}
override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning()}
}
This is what I want (Below One):
I have been playing around with custom activity indicators, so here's one way to do it
import UIKit
#IBDesignable class ActivityIndicatorCircle: UIView {
var timerInterval: Double = 0.1
var timer : Timer?
var endAngle: CGFloat = 0.0
var angleStep = CGFloat.pi / 20.0
var angleOffset = -CGFloat.pi / 2.0
var shapeLayer = CAShapeLayer()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
func startAnimating() {
superview?.bringSubview(toFront: self)
layer.cornerRadius = frame.width / 2
self.clipsToBounds = true
isHidden = false
timer?.invalidate()
timer = nil
timer = Timer.scheduledTimer(timeInterval: timerInterval,
target: self,
selector: #selector(self.updateCircle),
userInfo: nil,
repeats: true)
}
func stopAnimating() {
isHidden = true
timer?.invalidate()
timer = nil
}
func updateCircle() {
endAngle += angleStep
if endAngle > CGFloat.pi * 2.0 {
endAngle -= CGFloat.pi * 2.0
}
DispatchQueue.main.async {
self.shapeLayer.removeFromSuperlayer() // remove the previous version
let lineWidth: CGFloat = 6.0
let radius = self.frame.size.width / 2.0 // if the view is square, this gives us center as well
let circlePath = UIBezierPath(arcCenter: CGPoint(x: radius,y: radius), radius: radius - lineWidth, startAngle: self.angleOffset, endAngle: self.endAngle + self.angleOffset, clockwise: true)
self.shapeLayer.path = circlePath.cgPath
self.shapeLayer.position = CGPoint(x: 0, y: 0)
self.shapeLayer.fillColor = UIColor.clear.cgColor
self.shapeLayer.strokeColor = UIColor.blue.cgColor
self.shapeLayer.lineWidth = lineWidth
let one : NSNumber = 1
let two : NSNumber = 13
self.shapeLayer.lineDashPattern = [one,two]
self.shapeLayer.lineCap = kCALineCapRound
self.layer.addSublayer(self.shapeLayer)
}
}
}
to use this, add a UIView to your storyboard, and set the class to ActivityIndicatorCircle
To start it, call activityIndicatorCircle.startAnimating()
for circular loader you can use this library it will help you. https://github.com/naoyashiga/RPLoadingAnimation
Im still trying to learn this portion of things. I've looked around and read a few questions about it but truthfully I dont understand any of it.
I've got a circle class that creates and draws a circle
class CircleView: UIView {
var circleLayer: CAShapeLayer!
var isAnimating = false
override init(frame: CGRect) {
let fColor = UIColor.clear.cgColor
super.init(frame: frame)
self.backgroundColor = UIColor.clear
// Use UIBezierPath as an easy way to create the CGPath for the layer.
// The path should be the entire circle.
let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)
// Setup the CAShapeLayer with the path, colors, and line width
circleLayer = CAShapeLayer()
circleLayer.path = circlePath.cgPath
circleLayer.fillColor = UIColor.clear.cgColor
circleLayer.strokeColor = UIColor.init(rgbColorCodeRed: 230, green: 226, blue: 218, alpha: 1).cgColor
circleLayer.lineWidth = 9.0;
// Don't draw the circle initially
circleLayer.strokeEnd = 0.0
// Add the circleLayer to the view's layer's sublayers
layer.addSublayer(circleLayer)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setCircleClockwise(){
let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)
self.circleLayer.removeFromSuperlayer()
self.circleLayer = formatCirle(circlePath: circlePath)
self.layer.addSublayer(self.circleLayer)
}
func setCircleCounterClockwise(){
let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: false)
self.circleLayer.removeFromSuperlayer()
self.circleLayer = formatCirle(circlePath: circlePath)
self.layer.addSublayer(self.circleLayer)
}
func formatCirle(circlePath: UIBezierPath) -> CAShapeLayer{
let circleShape = CAShapeLayer()
circleShape.path = circlePath.cgPath
circleShape.fillColor = UIColor.clear.cgColor
circleShape.strokeColor = UIColor.init(rgbColorCodeRed: 230, green: 226, blue: 218, alpha: 1).cgColor
circleShape.lineWidth = 9.0;
circleShape.strokeEnd = 0.0
return circleShape
}
func animate(duration: TimeInterval){
self.isAnimating = true
self.animateCircleFull(duration: 1)
}
func endAnimate(){
self.isAnimating = false
}
func animateCircleFull(duration: TimeInterval) {
if self.isAnimating{
CATransaction.begin()
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = duration
animation.fromValue = 0
animation.toValue = 1
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
circleLayer.strokeEnd = 1.0
CATransaction.setCompletionBlock {
self.setCircleCounterClockwise()
self.animateCircleEmpty(duration: duration)
}
// Do the actual animation
circleLayer.add(animation, forKey: "animateCircle")
CATransaction.commit()
}
}
func animateCircleEmpty(duration: TimeInterval){
if self.isAnimating{
CATransaction.begin()
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = duration
animation.fromValue = 1
animation.toValue = 0
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
circleLayer.strokeEnd = 0
CATransaction.setCompletionBlock {
self.setCircleClockwise()
self.animateCircleFull(duration: duration)
}
// Do the actual animation
circleLayer.add(animation, forKey: "animateCircle")
CATransaction.commit()
}
}
Which is being called from my viewController with the below function. It all works fine but what i cant work out is how do i go about calling the endAnimation function on the same circle?
func addCircleView() {
let diceRoll = CGFloat(Int(arc4random_uniform(7))*50)
var circleWidth = CGFloat(100)
var circleHeight = circleWidth
var bgColor: UIColor = UIColor.init(rgbColorCodeRed: 230, green: 226, blue: 218, alpha: 1)
// Create a new CircleView
let circleView = CircleView(frame: CGRect(x: self.view.frame.width/2, y: self.view.frame.height-110, width: circleWidth, height: circleHeight))
//let test = CircleView(frame: CGRect(x: diceRoll, y: 0, width: circleWidth, height: circleHeight))
cv = circleView
view.addSubview(circleView)
// Animate the drawing of the circle over the course of 1 second
circleView.animate(duration: 1)
let imageName = "ButtonBackground.png"
let image = UIImage(named: imageName)
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: self.view.frame.width/2, y: self.view.frame.height-110, width: 100, height: 100)
view.addSubview(imageView)
view.bringSubview(toFront: circleView)
}
For calling endAnimation function on the same circle you have to declare property in your ViewController class.
import UIKit
class ViewController: UIViewController {
var circleView = CircleView()
override func viewDidLoad() {
super.viewDidLoad()
self.addCircleView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func addCircleView() {
let diceRoll = CGFloat(Int(arc4random_uniform(7))*50)
var circleWidth = CGFloat(100)
var circleHeight = circleWidth
var bgColor: UIColor = UIColor.init(rgbColorCodeRed: 230, green: 226, blue: 218, alpha: 1)
// Create a new CircleView
self.circleView = CircleView(frame: CGRect(x: self.view.frame.width/2, y: self.view.frame.height-110, width: circleWidth, height: circleHeight))
//let test = CircleView(frame: CGRect(x: diceRoll, y: 0, width: circleWidth, height: circleHeight))
cv = circleView
view.addSubview(circleView)
// Animate the drawing of the circle over the course of 1 second
circleView.animate(duration: 1)
let imageName = "ButtonBackground.png"
let image = UIImage(named: imageName)
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: self.view.frame.width/2, y: self.view.frame.height-110, width: 100, height: 100)
view.addSubview(imageView)
view.bringSubview(toFront: circleView)
//now you are able to call endAnimation function
self.circleView.endAnimate()
}
}
I am trying to draw 3 circles inside my 3 views but only the top view circle is drawn even doth the code is the same. I can't seem to understand where is the problem, that's why the two other circle are not there?
class ViewController: UIViewController {
///Views used to display the progress view
var topView: CircleView!
var middleView: CircleView!
var bottomView: CircleView!
override func viewDidLoad() {
super.viewDidLoad()
createThreeViews()
}
func createThreeViews(){
let viewHeight = self.view.bounds.height
let viewWidth = self.view.bounds.width
//Top View
let topTimerFrame = CGRect(x: 0, y: 0, width: viewWidth, height: 3/6 * viewHeight)
topView = CircleView(frame: topTimerFrame)
topView.backgroundColor = UIColor.redColor()
//Middle View
let middleTimerFrame = CGRect(x: 0, y: topTimerFrame.height, width: viewWidth, height: 2/6 * viewHeight)
middleView = CircleView(frame: middleTimerFrame)
middleView.backgroundColor = UIColor.blueColor()
//Bottom view
let bottomTimerFrame = CGRect(x: 0, y: topTimerFrame.height + middleTimerFrame.height, width: viewWidth, height: 1/6 * viewHeight)
bottomView = CircleView(frame: bottomTimerFrame)
bottomView.backgroundColor = UIColor.greenColor()
//add top circle and set constraint
self.view.addSubview(topView)
//add middle circle and set constraints
self.view.addSubview(middleView)
//add bottom circle and set constraints
self.view.addSubview(bottomView)
}
}
//class used to create the views and draw circles in them
class CircleView: UIView {
let π:CGFloat = CGFloat(M_PI)
let circle = CAShapeLayer()
var secondLayerColor: UIColor = UIColor.whiteColor()
//custom initializer
override init(frame: CGRect) {
super.init(frame: frame)
userInteractionEnabled = true
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
userInteractionEnabled = true
setup()
}
func setup() {
//draw the circle and add to layer
circle.frame = bounds
circle.lineWidth = CGFloat(4)
circle.fillColor = UIColor.whiteColor().CGColor
circle.strokeEnd = 1
layer.addSublayer(circle)
setupShapeLayer(circle)
}
override func layoutSubviews() {
super.layoutSubviews()
setupShapeLayer(circle)
}
func setupShapeLayer(shapeLayer: CAShapeLayer) {
shapeLayer.frame = bounds
let radius = frame.height/2 - circle.lineWidth/2
let startAngle = CGFloat(0)
let endAngle = 2*π
let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)
shapeLayer.path = path.CGPath
}
}
You are drawing your circle with an offset to your frame (the other two circles are drawn, but outside of the frames).
Change:
let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)
to:
let path = UIBezierPath(arcCenter: CGPoint(x: center.x , y: frame.height/2) , radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)
Btw. you probably want to change setupShapeLayer(circle) in setup() to setNeedsLayout(); layoutIfNeeded(), otherwise it will be drawn twice the first time.