5

I am trying to draw a pie chart with a segment array and an image in the center of each slice , I am have drawn the pie chart , but not able to add an image to the cashapelayer.I refered to the following post Swift: Add image to CAShapeLayer , but it did not solve my issue.

This is what I have tried.

override func draw(_ rect: CGRect) {
        //let context = UIGraphicsGetCurrentContext()
        let radius = min(frame.size.width,frame.size.height) * 0.5;
        let viewCenter = CGPoint(x: frame.size.width * 0.5, y: frame.size.height * 0.5)
        let valueCount = segments.reduce(0){ $0 + $1.value}
        var startAngle = -CGFloat(M_PI / 2)

        for segment in segments {
           let endAngle = startAngle + CGFloat(M_PI * 2) * (segment.value)/valueCount

            /*context?.move(to: CGPoint(x: viewCenter.x, y: viewCenter.y))
            context?.setFillColor(segment.color.cgColor)
            context?.setStrokeColor(UIColor.black.cgColor)
            context?.setLineWidth(3.0)
            context?.addArc(center: viewCenter, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)
            context?.fillPath()*/

            let pieSliceLayer = CAShapeLayer()
            let slicePath = UIBezierPath(arcCenter: viewCenter, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)
            pieSliceLayer.path = slicePath.cgPath
            pieSliceLayer.fillColor = segment.color.cgColor
            self.layer.addSublayer(pieSliceLayer)

            let halfAngle = startAngle + (endAngle - startAngle) * 0.5;
            let imagePositionValue = CGFloat(0.67)
            let segmentCenter = CGPoint(x:viewCenter.x+radius*imagePositionValue*cos(halfAngle),y:viewCenter.y+radius*imagePositionValue*sin(halfAngle))

            //If image is associated , add the image to the section
            let imageLayer = CAShapeLayer()
            let image = UIImage(named: segment.imageName)
            imageLayer.contents = image?.ciImage
            imageLayer.frame = CGRect(x: segmentCenter.x - 20, y: segmentCenter.y - 20, width: 20, height: 20)
            self.layer.addSublayer(imageLayer)
            startAngle = endAngle

        }
    }

I tried something similar in objective-C and it works fine ,

CALayer *imageLayer = (CALayer *)[[pieLayer sublayers] objectAtIndex:0];
    //Adding image
    CALayer *layer = [CALayer layer];
    layer.backgroundColor = [[UIColor clearColor] CGColor];
    layer.bounds = CGRectMake(imageLayer.bounds.origin.x +20 ,
                                  imageLayer.bounds.origin.y + 20, 15, 15);
    NSString *imageName = [NSString stringWithString:[[self.goalSettingsModel objectAtIndex:value] valueForKeyPath:@"imageName"]];
    layer.contents =  (id)[UIImage imageNamed:imageName].CGImage;
    [imageLayer addSublayer:layer];
    [imageLayer setNeedsDisplay];

I am not sure , if I have missed any settings for the calayer.

Community
  • 1
  • 1
  • Hi had tried doing it the same way as Objective - c snippet , to calculate the center of the arc path I am using the following code , I still do not see the image ,let boundingBox = pieSliceLayer.path?.boundingBox let centerPoint = CGPoint(x:(boundingBox?.origin.x)! + (boundingBox?.size.width)!/2,y:(boundingBox?.origin.y)!+(boundingBox?.size.height)!/2) – Vinodha Sundaramoorthy Oct 20 '16 at 23:59

2 Answers2

8

After setting the position attribute for the layer, the image was added properly to the shape layer ,

let imageLayer = CALayer()
imageLayer.backgroundColor = UIColor.clear.cgColor
imageLayer.bounds = CGRect(x: centerPoint.x, y: centerPoint.y , width: 15, height: 15)
imageLayer.position = CGPoint(x:centerPoint.x ,y:centerPoint.y)
imageLayer.contents = UIImage(named:segment.imageName)?.cgImage
self.layer.addSublayer(pieSliceLayer)
self.layer.addSublayer(imageLayer)
DareDevil
  • 902
  • 8
  • 21
2

A couple of things:

First, you only need to update the shape layer when the graph changes (call setNeedsDisplay) or when you have a layout change (viewDidLayoutSubviews). You don't need to implement drawRect unless you are going to do actually drawing. The layering system knowns how to render the shape layer and will render it as necessary.

Second, CAShapeLayer is for shapes. There are actually a whole bunch of CALayers and they all draw different things: https://www.raywenderlich.com/90919/great-calayer-tour-tech-talk-video . In the case of a plain old image, just do what your Objective C code is doing and use a separate CALayer to hold the image instead of CAShapeLayer. Add your image layer as a sublayer on top of or behind your graph shape layer, depending on your z-ordering preference.

Josh Homann
  • 15,933
  • 3
  • 30
  • 33
  • Thanks for the video link , it was very useful. I fixed the issue by setting the position of the CALayer. let imageLayer = CALayer() imageLayer.backgroundColor = UIColor.clear.cgColor imageLayer.bounds = CGRect(x: centerPoint.x, y: centerPoint.y , width: 15, height: 15) imageLayer.position = CGPoint(x:centerPoint.x ,y:centerPoint.y) imageLayer.contents = UIImage(named:segment.imageName)?.cgImage self.layer.addSublayer(pieSliceLayer) self.layer.addSublayer(imageLayer) – Vinodha Sundaramoorthy Oct 21 '16 at 21:21