Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding image to a CAShapeLayer Swift 3

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.

like image 781
Vinodha Sundaramoorthy Avatar asked Oct 20 '16 22:10

Vinodha Sundaramoorthy


2 Answers

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)
like image 121
Vinodha Sundaramoorthy Avatar answered Oct 21 '22 10:10

Vinodha Sundaramoorthy


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.

like image 30
Josh Homann Avatar answered Oct 21 '22 11:10

Josh Homann