Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

troop231

macrumors 603
Original poster
Jan 20, 2010
5,822
553
I've been able to get pretty far with what I've been wanting to accomplish, and that's to replicate iOS's built in circular photo cropper for the built in contacts app.

However, I'm stuck at trying to get my CAShapeLayers made correctly. I'm trying to make a transparent 320 px diameter circle and the rest of the view filled with an 0.9 alpha black background. The circle and rectangle are in the right place, but, the circle is not completely transparent like I need it to be.

I'm lost as to how to fix this. I appreciate your help! Code and screenshot:

Edit: Resolved code below:

Code:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if ([navigationController.viewControllers count] == 3)
    {
        CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
        
        UIView *plCropOverlay = [[[viewController.view.subviews objectAtIndex:1]subviews] objectAtIndex:0];
        
        plCropOverlay.hidden = YES;
        
        int position = 0;
        
        if (screenHeight == 568)
        {
            position = 124;
        }
        else
        {
            position = 80;
        }

        CAShapeLayer *circleLayer = [CAShapeLayer layer];

        UIBezierPath *path2 = [UIBezierPath bezierPathWithOvalInRect:
                               CGRectMake(0.0f, position, 320.0f, 320.0f)];
        [path2 setUsesEvenOddFillRule:YES];
        
        [circleLayer setPath:[path2 CGPath]];
        
        [circleLayer setFillColor:[[UIColor clearColor] CGColor]];
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 320, screenHeight-72) cornerRadius:0];
        
        [path appendPath:path2];
        [path setUsesEvenOddFillRule:YES];
        
        CAShapeLayer *fillLayer = [CAShapeLayer layer];
        fillLayer.path = path.CGPath;
        fillLayer.fillRule = kCAFillRuleEvenOdd;
        fillLayer.fillColor = [UIColor blackColor].CGColor;
        fillLayer.opacity = 0.8;
        [viewController.view.layer addSublayer:fillLayer];
        
        UILabel *moveLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 10, 320, 50)];
        [moveLabel setText:@"Move and Scale"];
        [moveLabel setTextAlignment:NSTextAlignmentCenter];
        [moveLabel setTextColor:[UIColor whiteColor]];
        
        [viewController.view addSubview:moveLabel];
    }
}

aJ9xfaI.png
 
Last edited:

tacotester1

macrumors member
Dec 19, 2013
99
0
that's awesome!
I think people will like that

I'm still working on a square crop. do you know of any reference code for
an instagram like crop from a library or camera photo?

thanks
 

Duncan C

macrumors 6502a
Jan 21, 2008
853
0
Northern Virginia
You need to experiment with a filled bezier path (or CGPath) that is a rectangle with a circle inside it. With the correct winding rule you should be able to create the shape you want.

UIBezierPath is a little easier to deal with, and it has a property that lets you reference the internal CGPath object.

Here's what I would do:

Open a path

Draw your rectangle
Draw your circle
Close the path.

Add the path as the path of your shape layer.

The default fill rule of kCAFillRuleNonZero should work (I think) but if not, try the other rule, kCAFillRuleEvenOdd.

Set the fill color to your 90% opaque black color:

Code:
myShapeLayer.fillColor = 
  [UIColor colorWithRed: 0 green: 0 blue: 0 alpha: .9].CGColor;

I've been able to get pretty far with what I've been wanting to accomplish, and that's to replicate iOS's built in circular photo cropper for the built in contacts app.

However, I'm stuck at trying to get my CAShapeLayers made correctly. I'm trying to make a transparent 320 px diameter circle and the rest of the view filled with an 0.9 alpha black background. The circle and rectangle are in the right place, but, the circle is not completely transparent like I need it to be.

I'm lost as to how to fix this. I appreciate your help! Code and screenshot:

Edit: Resolved code below:

Code:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if ([navigationController.viewControllers count] == 3)
    {
        CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
        
        UIView *plCropOverlay = [[[viewController.view.subviews objectAtIndex:1]subviews] objectAtIndex:0];
        
        plCropOverlay.hidden = YES;
        
        int position = 0;
        
        if (screenHeight == 568)
        {
            position = 124;
        }
        else
        {
            position = 80;
        }

        CAShapeLayer *circleLayer = [CAShapeLayer layer];

        UIBezierPath *path2 = [UIBezierPath bezierPathWithOvalInRect:
                               CGRectMake(0.0f, position, 320.0f, 320.0f)];
        [path2 setUsesEvenOddFillRule:YES];
        
        [circleLayer setPath:[path2 CGPath]];
        
        [circleLayer setFillColor:[[UIColor clearColor] CGColor]];
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 320, screenHeight-72) cornerRadius:0];
        
        [path appendPath:path2];
        [path setUsesEvenOddFillRule:YES];
        
        CAShapeLayer *fillLayer = [CAShapeLayer layer];
        fillLayer.path = path.CGPath;
        fillLayer.fillRule = kCAFillRuleEvenOdd;
        fillLayer.fillColor = [UIColor blackColor].CGColor;
        fillLayer.opacity = 0.8;
        [viewController.view.layer addSublayer:fillLayer];
        
        UILabel *moveLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 10, 320, 50)];
        [moveLabel setText:@"Move and Scale"];
        [moveLabel setTextAlignment:NSTextAlignmentCenter];
        [moveLabel setTextColor:[UIColor whiteColor]];
        
        [viewController.view addSubview:moveLabel];
    }
}

aJ9xfaI.png
 

irockit88

macrumors newbie
May 13, 2014
1
0
Quick Question.

What header/source files did you have to add to the code for it to work correctly?
 

waterskier2007

macrumors 68000
Jun 19, 2007
1,871
228
Novi, MI
Just one quick pointer, instead of using a vertical position, couldn't you use CGRectInset() to make the rectangle for your oval?
 

venkadesh

macrumors newbie
Nov 3, 2015
1
0
You need to experiment with a filled bezier path (or CGPath) that is a rectangle with a circle inside it. With the correct winding rule you should be able to create the shape you want.

UIBezierPath is a little easier to deal with, and it has a property that lets you reference the internal CGPath object.

Here's what I would do:

Open a path

Draw your rectangle
Draw your circle
Close the path.

Add the path as the path of your shape layer.

The default fill rule of kCAFillRuleNonZero should work (I think) but if not, try the other rule, kCAFillRuleEvenOdd.

Set the fill color to your 90% opaque black color:

Code:
myShapeLayer.fillColor =
  [UIColor colorWithRed: 0 green: 0 blue: 0 alpha: .9].CGColor;


Can you please tell me how to crop the original image with the above circular overlay ?
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.