CAShapeLayers added to a Stackview subviews is rendering only on one subview - ios

The calorieView and timeView are expected to display a black circle. But
the distanceView is displaying the expected black circle.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a
let allViews: [UIView] = [distanceView, calorieView, timeView]
let stackView = UIStackView(arrangedSubviews: allViews)
stackView.frame = view.frame
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.spacing = 0
let y1 = CGFloat(0)
let y2 = view.frame.height / 3
let y3 = y2 * 2
distanceView.frame = CGRect(x: 0, y: y1, width: view.frame.width, height: y2)
calorieView.frame = CGRect(x: 0, y: y2, width: view.frame.width, height: y2)
timeView.frame = CGRect(x: 0, y: y3, width: view.frame.width, height: y2)
distanceView.layer.addSublayer(self.createViewLayer(localView: distanceView))
calorieView.layer.addSublayer(self.createViewLayer(localView: calorieView))
timeView.layer.addSublayer(createViewLayer(localView: timeView))
view.addSubview(stackView)
}
func createCircleLayer(center: CGPoint, radius: CGFloat, clockWise:Bool) -> CAShapeLayer {
let shapeLayer = CAShapeLayer()
let path = UIBezierPath
(arcCenter: center, radius: radius, startAngle: 0,
endAngle: 2 * CGFloat.pi, clockwise: clockWise)
shapeLayer.path = path.cgPath
return shapeLayer
}
func createViewLayer(localView: UIView) -> CAShapeLayer {
print(localView)
var viewLayer = CAShapeLayer()
viewLayer.frame = localView.frame
let center = localView.center
print(center)
let radius = localView.frame.height / 3
let isClockwise = true
viewLayer = self.createCircleLayer
(center: center, radius: radius, clockWise: isClockwise)
return viewLayer
}
}

What you are doing wrong is setting y coordinates for your views, basically doing the work that vertical UIStackView will do.
When using a stack view all views should have x and y coordinates set to 0,0, since it will be up to UIStackView to position arrangedViews according to it's settings.
Just change your frames to have origin = (0,0) or don't use UIStackView and use the calculated y coordinates...

Related

How to curve a view edge to looks like an arc

I want to make an view to looks like this:
If there is a way, something like define point A, point B, with a predefined angle.
The only solution that I found is to make a giant rounded view and insert it as a subview of another view with clipToBounds = true. But this has some problems in multiple screen sizes because I'm using constraints.
Edit1: After some search, i'm trying to create that view with CAShapeLayer, without success. I'm creating that view by storyboard, with constraints, that view is connected by IBOutlet and your leading constraint too. Here's the code:
On viewDidLoad:
self.cnstRoundedLeading.constant = -(self.vwRounded.frame.width/3)
let maskPath : UIBezierPath = UIBezierPath(roundedRect: CGRect(x: self.vwRounded.bounds.minX*4,
y: self.vwRounded.bounds.minY*4,
width: self.vwRounded.bounds.width*4,
height: self.vwRounded.bounds.height*4),
byRoundingCorners: .topLeft,
cornerRadii: CGSize(width: self.vwRounded.frame.size.width*2,
height: self.vwRounded.frame.size.height))
let maskLayer : CAShapeLayer = CAShapeLayer()
maskLayer.frame = self.vwRounded.bounds
maskLayer.path = maskPath.cgPath
self.vwRounded.layer.mask = maskLayer
And on viewWillLayoutSubviews:
gradient2.colors = [startColorBlue.cgColor, endColorBlue.cgColor]
gradient2.locations = [0.0, 1.0]
gradient2.startPoint = CGPoint(x: 0, y: 1)
gradient2.endPoint = CGPoint(x: 1, y: 0)
vwRounded.applyGradient(gradient2)
applyGradient it's a extension of UIView:
func applyGradient(_ gradient: CAGradientLayer) -> Void {
gradient.frame = self.bounds
self.layer.insertSublayer(gradient, at: 0)
}
Does not work properly, i don't know the right way to construct that 'arc edge' effect
You can create that shape - and use it as a mask - with a UIBezierPath (the black border is showing the actual view frame):
Basically,
Find the midpoint of the line from pt1 to pt2.
Find the point perpendicular to that line, with a distance from the line that makes the curve look the way you want. Using the same length as half-the-line-length is what's shown here.
Create a UIBezierPath, connecting pt1 to pt2 with a quadratic curve.
Here is example code that you can run directly in a Playground page. I based the pt1 and pt2 y-positions based on the image you posted... If you change the frame of the view, it will maintain the proportions you've shown.
import PlaygroundSupport
import UIKit
class TestViewController: UIViewController {
override public var preferredContentSize: CGSize {
get { return CGSize(width: 800, height: 800) }
set { super.preferredContentSize = newValue }
}
let myPlainView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let myBorderView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
func customCurvedPath(for rect: CGRect) -> UIBezierPath {
// curve start point Y is 490/544ths of the height of the view
let curveStartPoint = CGPoint(x: 0.0, y: rect.size.height * 490.0 / 544.0)
// curve end point Y is 22/544ths of the height of the view
let curveEndPoint = CGPoint(x: rect.size.width, y: rect.size.height * 22.0 / 544.0)
var x1 = curveStartPoint.x
var y1 = curveStartPoint.y
let x2 = curveEndPoint.x
let y2 = curveEndPoint.y
// get the midpoint of the line from x1,y1 to x2,y2
x1 = (x1 + x2) / 2.0
y1 = (y1 + y2) / 2.0
// get the length of half the line (midpoint to endpoint)
var dx = x1 - x2
var dy = y1 - y2
let dist = sqrt(dx*dx + dy*dy)
// use length of helf the line for distance from line
// increase or decrease this value to get the desired curve
let distFromLine = dist
dx /= dist
dy /= dist
// get perpendicular point at distFromLine
let x3 = x1 - (distFromLine/2)*dy
let y3 = y1 + (distFromLine/2)*dx
let curveControlPoint = CGPoint(x: x3, y: y3)
let myBezier = UIBezierPath()
// pt1
myBezier.move(to: curveStartPoint)
// quad curve to pt2
myBezier.addQuadCurve(to: curveEndPoint, controlPoint: curveControlPoint)
// line to pt3 (bottom right corner)
myBezier.addLine(to: CGPoint(x: rect.width, y: rect.height))
// line to pt4 (bottom left corner)
myBezier.addLine(to: CGPoint(x: 0.0, y: rect.height))
// close the path (automatically add a line from bottom left corner to curve start point)
myBezier.close()
return myBezier
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let vwWidth = CGFloat(710.0)
let vwHeight = CGFloat(544.0)
view.addSubview(myBorderView)
myBorderView.backgroundColor = .clear
NSLayoutConstraint.activate([
myBorderView.widthAnchor.constraint(equalToConstant: vwWidth + 2.0),
myBorderView.heightAnchor.constraint(equalToConstant: vwHeight + 2.0),
myBorderView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
myBorderView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
myBorderView.layer.borderWidth = 2.0
// comment this next line (or set to false) to see the actual view frame
myBorderView.isHidden = true
view.addSubview(myPlainView)
myPlainView.backgroundColor = .red
NSLayoutConstraint.activate([
myPlainView.widthAnchor.constraint(equalToConstant: vwWidth),
myPlainView.heightAnchor.constraint(equalToConstant: vwHeight),
myPlainView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
myPlainView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
let bezPath = customCurvedPath(for: CGRect(x: 0, y: 0, width: vwWidth, height: vwHeight))
// add the bezier path as a layer mask
let maskForPath = CAShapeLayer()
maskForPath.path = bezPath.cgPath
myPlainView.layer.mask = maskForPath
}
}
let viewController = TestViewController()
PlaygroundPage.current.liveView = viewController
As I mentioned, this would work better as part of a custom view class, as you could override layoutSubviews() to keep the path shape consistent.
Here's an example using a gradient layer + a layer mask in a custom view, set to 300 x 250:
And the Playground-runnable source:
import PlaygroundSupport
import UIKit
class MaskedGradientView: UIView {
var gradLayer: CAGradientLayer!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
gradLayer = CAGradientLayer()
gradLayer.colors = [UIColor.blue.cgColor, UIColor.cyan.cgColor]
gradLayer.locations = [0.0, 1.0]
gradLayer.startPoint = CGPoint(x: 0, y: 1)
gradLayer.endPoint = CGPoint(x: 1, y: 0)
layer.addSublayer(gradLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
let rect = self.bounds
gradLayer.frame = self.bounds
// curve start point Y is 490/544ths of the height of the view
let curveStartPoint = CGPoint(x: 0.0, y: rect.size.height * 490.0 / 544.0)
// curve end point Y is 22/544ths of the height of the view
let curveEndPoint = CGPoint(x: rect.size.width, y: rect.size.height * 22.0 / 544.0)
var x1 = curveStartPoint.x
var y1 = curveStartPoint.y
let x2 = curveEndPoint.x
let y2 = curveEndPoint.y
// get the midpoint of the line from x1,y1 to x2,y2
x1 = (x1 + x2) / 2.0
y1 = (y1 + y2) / 2.0
// get the length of half the line (midpoint to endpoint)
var dx = x1 - x2
var dy = y1 - y2
let dist = sqrt(dx*dx + dy*dy)
// use length of helf the line for distance from line
// increase or decrease this value to get the desired curve
let distFromLine = dist
dx /= dist
dy /= dist
// get perpendicular point at distFromLine
let x3 = x1 - (distFromLine/2)*dy
let y3 = y1 + (distFromLine/2)*dx
let curveControlPoint = CGPoint(x: x3, y: y3)
let myBezier = UIBezierPath()
// pt1
myBezier.move(to: curveStartPoint)
// quad curve to pt2
myBezier.addQuadCurve(to: curveEndPoint, controlPoint: curveControlPoint)
// line to pt3 (bottom right corner)
myBezier.addLine(to: CGPoint(x: rect.width, y: rect.height))
// line to pt4 (bottom left corner)
myBezier.addLine(to: CGPoint(x: 0.0, y: rect.height))
// close the path (automatically add a line from bottom left corner to curve start point)
myBezier.close()
// add the bezier path as a layer mask
let maskForPath = CAShapeLayer()
maskForPath.path = myBezier.cgPath
layer.mask = maskForPath
}
}
class TestViewController: UIViewController {
override public var preferredContentSize: CGSize {
get { return CGSize(width: 400, height: 400) }
set { super.preferredContentSize = newValue }
}
let myMaskedGradientView: MaskedGradientView = {
let v = MaskedGradientView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let myPlainView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .blue
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(myMaskedGradientView)
NSLayoutConstraint.activate([
myMaskedGradientView.widthAnchor.constraint(equalToConstant: 300.0),
myMaskedGradientView.heightAnchor.constraint(equalToConstant: 250.0),
myMaskedGradientView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
myMaskedGradientView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
}
}
let viewController = TestViewController()
PlaygroundPage.current.liveView = viewController

Corner radius image Swift

I'm trying to make this corner radius image...it's not exactly the same shape of the image..any easy answer instead of trying random numbers of width and height ?
thanks alot
let rectShape = CAShapeLayer()
rectShape.bounds = self.mainImg.frame
rectShape.position = self.mainImg.center
rectShape.path = UIBezierPath(roundedRect: self.mainImg.bounds, byRoundingCorners: [.bottomLeft , .bottomRight ], cornerRadii: CGSize(width: 50, height: 4)).cgPath
You can use QuadCurve to get the design you want.
Here is a Swift #IBDesignable class that lets you specify the image and the "height" of the rounding in Storyboard / Interface Builder:
#IBDesignable
class RoundedBottomImageView: UIView {
var imageView: UIImageView!
#IBInspectable var image: UIImage? {
didSet { self.imageView.image = image }
}
#IBInspectable var roundingValue: CGFloat = 0.0 {
didSet {
self.setNeedsLayout()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
doMyInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
doMyInit()
}
func doMyInit() {
imageView = UIImageView()
imageView.backgroundColor = UIColor.red
imageView.contentMode = UIViewContentMode.scaleAspectFill
addSubview(imageView)
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = self.bounds
let rect = self.bounds
let y:CGFloat = rect.size.height - roundingValue
let curveTo:CGFloat = rect.size.height + roundingValue
let myBezier = UIBezierPath()
myBezier.move(to: CGPoint(x: 0, y: y))
myBezier.addQuadCurve(to: CGPoint(x: rect.width, y: y), controlPoint: CGPoint(x: rect.width / 2, y: curveTo))
myBezier.addLine(to: CGPoint(x: rect.width, y: 0))
myBezier.addLine(to: CGPoint(x: 0, y: 0))
myBezier.close()
let maskForPath = CAShapeLayer()
maskForPath.path = myBezier.cgPath
layer.mask = maskForPath
}
}
Result with 300 x 200 image view, rounding set to 40:
Edit - (3.5 years later)...
To answer #MiteshDobareeya comment, we can switch the rounded edge from Bottom to Top by transforming the bezier path:
let c = CGAffineTransform(scaleX: 1, y: -1).concatenating(CGAffineTransform(translationX: 0, y: bounds.size.height))
myBezier.apply(c)
It's been quite a while since this answer was originally posted, so a few changes:
subclass UIImageView directly - no need to make it a UIView with an embedded UIImageView
add a Bool roundTop var
if set to False (the default), we round the Bottom
if set to True, we round the Top
re-order and "name" our path points for clarity
So, the basic principle:
We create a UIBezierPath and:
move to pt1
add a line to pt2
add a line to pt3
add a quad-curve to pt4 with controlPoint
close the path
use that path for a CAShapeLayer mask
the result:
If we want to round the Top, after closing the path we can apply apply a scale transform using -1 as the y value to vertically mirror it. Because that transform mirror it at "y-zero" we also apply a translate transform to move it back down into place.
That gives us:
Here's the updated class:
#IBDesignable
class RoundedTopBottomImageView: UIImageView {
#IBInspectable var roundingValue: CGFloat = 0.0 {
didSet {
self.setNeedsLayout()
}
}
#IBInspectable var roundTop: Bool = false {
didSet {
self.setNeedsLayout()
}
}
override func layoutSubviews() {
super.layoutSubviews()
let r = bounds
let myBezier = UIBezierPath()
let pt1: CGPoint = CGPoint(x: r.minX, y: r.minY)
let pt2: CGPoint = CGPoint(x: r.maxX, y: r.minY)
let pt3: CGPoint = CGPoint(x: r.maxX, y: r.maxY - roundingValue)
let pt4: CGPoint = CGPoint(x: r.minX, y: r.maxY - roundingValue)
let controlPoint: CGPoint = CGPoint(x: r.midX, y: r.maxY + roundingValue)
myBezier.move(to: pt1)
myBezier.addLine(to: pt2)
myBezier.addLine(to: pt3)
myBezier.addQuadCurve(to: pt4, controlPoint: controlPoint)
myBezier.close()
if roundTop {
// if we want to round the Top instead of the bottom,
// flip the path vertically
let c = CGAffineTransform(scaleX: 1, y: -1) //.concatenating(CGAffineTransform(translationX: 0, y: bounds.size.height))
myBezier.apply(c)
}
let maskForPath = CAShapeLayer()
maskForPath.path = myBezier.cgPath
layer.mask = maskForPath
}
}
You can try with UIView extension. as
extension UIView {
func setBottomCurve(){
let offset = CGFloat(self.frame.size.height + self.frame.size.height/1.8)
let bounds = self.bounds
let rectBounds = CGRect(x: bounds.origin.x,
y: bounds.origin.y ,
width: bounds.size.width,
height: bounds.size.height / 2)
let rectPath = UIBezierPath(rect: rectBounds)
let ovalBounds = CGRect(x: bounds.origin.x - offset / 2,
y: bounds.origin.y ,
width: bounds.size.width + offset,
height: bounds.size.height)
let ovalPath = UIBezierPath(ovalIn: ovalBounds)
rectPath.append(ovalPath)
let maskLayer = CAShapeLayer()
maskLayer.frame = bounds
maskLayer.path = rectPath.cgPath
self.layer.mask = maskLayer
}
}
& use it in viewWillAppear like methods where you can get actual frame of UIImageView.
Usage:
override func viewWillAppear(_ animated: Bool) {
//use it in viewWillAppear like methods where you can get actual frame of UIImageView
myImageView.setBottomCurve()
}

Transparent semi circle in top center of UIView in swift [duplicate]

I want to crop an UIView with bottom and top of repeated semi circle like this image
I had been working on your question and here is my results, you need create a UIBezierPath and apply to your desired view, use this code for that
Function to generate the desired BezierPath
func pathSemiCirclesPathForView(givenView: UIView, ciclesRadius:CGFloat = 10, circlesDistance : CGFloat = 2) ->UIBezierPath
{
let width = givenView.frame.size.width
let height = givenView.frame.size.height
let semiCircleWidth = CGFloat(ciclesRadius*2)
let semiCirclesPath = UIBezierPath()
semiCirclesPath.move(to: CGPoint(x:0, y:0))
var x = CGFloat(0)
var i = 0
while x < width {
x = (semiCircleWidth) * CGFloat(i) + (circlesDistance * CGFloat(i))
let pivotPoint = CGPoint(x: x + semiCircleWidth/2, y: height)
semiCirclesPath.addArc(withCenter: pivotPoint, radius: ciclesRadius, startAngle: -180 * .pi / 180.0, endAngle: 0 * .pi / 180.0, clockwise: true)
semiCirclesPath.addLine(to: CGPoint(x: semiCirclesPath.currentPoint.x + circlesDistance, y: height))
i += 1
}
semiCirclesPath.addLine(to: CGPoint(x:width,y: 0))
i = 0
while x > 0 {
x = width - (semiCircleWidth) * CGFloat(i) - (circlesDistance * CGFloat(i))
let pivotPoint = CGPoint(x: x - semiCircleWidth/2, y: 0)
semiCirclesPath.addArc(withCenter: pivotPoint, radius: ciclesRadius, startAngle: 0 * .pi / 180.0, endAngle: -180 * .pi / 180.0, clockwise: true)
semiCirclesPath.addLine(to: CGPoint(x: semiCirclesPath.currentPoint.x - circlesDistance, y: 0))
i += 1
}
semiCirclesPath.close()
return semiCirclesPath
}
Function to apply the BezierPath to any View
func applySemiCircleEffect(givenView: UIView){
let shapeLayer = CAShapeLayer(layer: givenView.layer)
shapeLayer.path = self.pathSemiCirclesPathForView(givenView: givenView).cgPath
shapeLayer.frame = givenView.bounds
shapeLayer.masksToBounds = true
shapeLayer.shadowOpacity = 1
shapeLayer.shadowColor = UIColor.black.cgColor
shapeLayer.shadowOffset = CGSize(width: 0, height: 0)
shapeLayer.shadowRadius = 3
givenView.layer.mask = shapeLayer
}
Use it
#IBOutlet weak var customView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.applySemiCircleEffect(givenView: customView)
}
This is how it looks
Hope this helps you, Happy Coding

Drawing circles proportionally to containing view in swift

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.

iOS Swift Xcode 6: CGAffineTransformRotate with auto-layout anchorpoint

I'm making an app with a rotatable pie chart. However, my pie chart rotates around (0,0) and I can't find a solution to make it rotate around its center.
Some code:
// DRAWING CODE
// Set constants for piePieces
let radius: CGFloat = CGFloat(screen.width * 0.43)
let pi: CGFloat = 3.1415926535
let sliceRad: CGFloat = 2.0 * pi / CGFloat(categoryArray.count)
var currentAngle: CGFloat = -0.5 * sliceRad - 0.5 * pi
let center: CGPoint = CGPoint(x: screen.width / 2.0, y: radius)
println("Center point: \(center)")
//container!.layer.anchorPoint = CGPoint(x: screen.width, y: radius)
// Draw all pie charts, add them to container (chartView)
for category in categoryArray {
let slice = UIView()
slice.frame = self.frame
let shapeLayer = CAShapeLayer()
let scoreIndex = CGFloat(category.calculateScoreIndex())
let sliceRadius: CGFloat = scoreIndex * radius
// Draw the path
var path:UIBezierPath = UIBezierPath()
path.moveToPoint(center)
path.addLineToPoint(CGPoint(x: center.x + sliceRadius * cos(currentAngle), y: center.y + sliceRadius * sin(currentAngle)))
path.addArcWithCenter(center, radius: sliceRadius, startAngle: currentAngle, endAngle: currentAngle + sliceRad, clockwise: true)
path.addLineToPoint(center)
path.closePath()
// For next slice, add 2*pi Rad / n categories
currentAngle += sliceRad
// Add path to shapeLayer
shapeLayer.path = path.CGPath
//shapeLayer.frame = self.frame
shapeLayer.fillColor = SurveyColors().getColor(category.categoryIndex).CGColor
shapeLayer.anchorPoint = center
// Add shapeLayer to sliceView
slice.layer.addSublayer(shapeLayer)
// Add slice to chartView
container!.addSubview(slice)
}
self.addSubview(container!)
//container!.center = center
container!.layer.anchorPoint = center
}
I sheduled a NSTimer to perform a rotation every 2 seconds (for testing purposes):
func rotate() {
let t: CGAffineTransform = CGAffineTransformRotate(container!.transform, -0.78)
container!.transform = t;
}
The pie chart rotates around (0,0). What is going wrong?
I believe a good solution to your problem would be to set a container view:
container = UIView()
container!.frame = CGRect(x: 0.0, y: 0.0, width: screen.width, height: screen.width)
container!.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
container!.backgroundColor = UIColor.clearColor()
And then add all slices to this container view:
let slice = UIView()
slice.frame = CGRect(x: 0.0 , y: 0.0 , width: container!.bounds.width, height: container!.bounds.height)
let shapeLayer = CAShapeLayer()
let scoreIndex = CGFloat(category.calculateScoreIndex())
let sliceRadius: CGFloat = scoreIndex * radius
/*
*/
container!.addSubview(slice)
Also, make sure not to add your chart as a subview to a view with auto-layout constraints (which might interfere with you CGAffineTransformRotate).

Resources