iosuiviewcore-graphicscgpathcashapelayer

Scale CGPath to Fit UIVIew


I need to know how I can scale down or up CGPath so it can fit UIView?

Set the background color to yellow, To see the view clearly

self.frame = CGRectMake(0, 0, 181, 154);
self.backgroundColor = [UIColor yellowColor];

Draw CAShapeLayer

CAShapeLayer *shape = [CAShapeLayer layer];
shape.path = aPath;

shape.strokeColor = [[UIColor blueColor] CGColor];
shape.lineWidth = 5;
shape.fillColor = [[UIColor clearColor] CGColor];

[self.layer addSublayer:shape];

Then I get This: enter image description here

What I want is this: enter image description here

Thank you Developers :)


Solution

  • I'm assuming that you want to completely fill (without distorting) the shape to fit the view you. I'm also assuming that you have a shape layer with the path already, since the question is tagged CAShapeLayer.


    In this case you would get the bounding box of the shape layers path and calculate the appropriate scale factor to apply to the path.

    Depending on the aspect ratio of the shape and the view it is supposed to fit it will either be the widths or heights that determine the scale factor.

    Once you have the scale factor you will create a transform to apply to the path. Since the path may not start at (0,0) you also translate (move) the path in the transform to the bounding boxes origin.

    If you want the new path to be in the center of the view you need to calculate how much to move it. You can comment out that code if you don't need it but I included it for other people reading this answer as well.

    Finally you have a new path so all you have to do is to create a new shape layer, assign the path, style it appropriately and add it to the view.

    // I'm assuming that the view and original shape layer is already created
    CGRect boundingBox = CGPathGetBoundingBox(shapeLayer.path);
    
    CGFloat boundingBoxAspectRatio = CGRectGetWidth(boundingBox)/CGRectGetHeight(boundingBox);
    CGFloat viewAspectRatio = CGRectGetWidth(viewToFitIn.frame)/CGRectGetHeight(viewToFitIn.frame);
    
    CGFloat scaleFactor = 1.0;
    if (boundingBoxAspectRatio > viewAspectRatio) {
        // Width is limiting factor
        scaleFactor = CGRectGetWidth(viewToFitIn.frame)/CGRectGetWidth(boundingBox);
    } else {
        // Height is limiting factor
        scaleFactor = CGRectGetHeight(viewToFitIn.frame)/CGRectGetHeight(boundingBox);
    }
    
    
    // Scaling the path ...
    CGAffineTransform scaleTransform = CGAffineTransformIdentity;
    // Scale down the path first
    scaleTransform = CGAffineTransformScale(scaleTransform, scaleFactor, scaleFactor);
    // Then translate the path to the upper left corner
    scaleTransform = CGAffineTransformTranslate(scaleTransform, -CGRectGetMinX(boundingBox), -CGRectGetMinY(boundingBox));
    
    // If you want to be fancy you could also center the path in the view
    // i.e. if you don't want it to stick to the top.
    // It is done by calculating the heigth and width difference and translating
    // half the scaled value of that in both x and y (the scaled side will be 0)
    CGSize scaledSize = CGSizeApplyAffineTransform(boundingBox.size, CGAffineTransformMakeScale(scaleFactor, scaleFactor));
    CGSize centerOffset = CGSizeMake((CGRectGetWidth(viewToFitIn.frame)-scaledSize.width)/(scaleFactor*2.0),
                                     (CGRectGetHeight(viewToFitIn.frame)-scaledSize.height)/(scaleFactor*2.0));
    scaleTransform = CGAffineTransformTranslate(scaleTransform, centerOffset.width, centerOffset.height);
    // End of "center in view" transformation code
    
    CGPathRef scaledPath = CGPathCreateCopyByTransformingPath(shapeLayer.path,
                                                              &scaleTransform);
    
    // Create a new shape layer and assign the new path
    CAShapeLayer *scaledShapeLayer = [CAShapeLayer layer];
    scaledShapeLayer.path = scaledPath;
    scaledShapeLayer.fillColor = [UIColor blueColor].CGColor;
    [viewToFitIn.layer addSublayer:scaledShapeLayer];
    
    CGPathRelease(scaledPath); // release the copied path
    

    In my example code (and shape) it looked like this

    enter image description here