Cropping an Image to the shape of an Overlay - iOS

DesperateLearner picture DesperateLearner · Sep 10, 2014 · Viewed 7k times · Source

I'm adding the overlay using pickerController's view and the uinavigationcontrollerdelegate as below.

-(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];

  }
}

When the overlay defined above is added, I tend to get this view:

enter image description here

I can crop the image exactly to a square using a defined CGRect.

   CGImageRef imageRef = CGImageCreateWithImageInRect([imageToCrop CGImage], rect);

   UIImage *cropped = [UIImage imageWithCGImage:imageRef];
   CGImageRelease(imageRef);

How about approaching this problem where there is a circular overlay and imagePickers editing property is YES? I can zoom in and zoom out of the pic. How can i make use of the BezierPath here?

Answer

Fattie picture Fattie · Sep 17, 2014

the short answer to your question addClip , but you mention you're a beginner, so here's all the steps from A to Z!!

Firstly, try this category, see if it helps. (If you're not familiar w/ categories have a google or just ask here in a comment.)

-(UIImage *)doMask
    {
    UIImage *maskImage = [UIImage imageNamed:@"yourMask.png"];
    
    CGImageRef maskRef = maskImage.CGImage;
    
    CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
        CGImageGetHeight(maskRef),
        CGImageGetBitsPerComponent(maskRef),
        CGImageGetBitsPerPixel(maskRef),
        CGImageGetBytesPerRow(maskRef),
        CGImageGetDataProvider(maskRef), NULL, false);
    
    CGImageRef maskedImageRef = CGImageCreateWithMask([self CGImage], mask);
    UIImage *maskedImage = [UIImage imageWithCGImage:maskedImageRef];
    
    CGImageRelease(mask);
    CGImageRelease(maskedImageRef);
    
    return maskedImage;
    }

just create (I mean in photoshop) a png mask, and get familiar with that process.

I encourage you to master that process first...

Here are critical categories that will help...

-(UIImage *)becomeSquare
    {
    CGSize imageSize = self.size;
    CGFloat width = imageSize.width;
    CGFloat height = imageSize.height;
    
    UIImage *result = self;
    
    if (width != height)
        {
        CGFloat newDimension = MIN(width, height);
        CGFloat widthOffset = (width - newDimension) / 2;
        CGFloat heightOffset = (height - newDimension) / 2;
        
        UIGraphicsBeginImageContextWithOptions(
            CGSizeMake(newDimension, newDimension), NO, 0. );
        
        [result drawAtPoint:CGPointMake(-widthOffset, -heightOffset)
            blendMode:kCGBlendModeCopy alpha:1. ];
        result = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        }
    
    return result;
    }

and a couple more ...

-(UIImage *)doScale
    {
    UIImage *result = self;
    
    CGSize size = CGSizeMake(320,320);
    UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
    [result drawInRect:CGRectMake(0.0f, 0.0f, size.width, size.height)];
    result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return result;
    }

-(UIImage *)scaled640AnyShape
    {
    if ( self.size.height < 5.0 ) return nil;
    if ( self.size.width < 5.0 ) return nil;
    
    UIImage *result = self;
    
    float widthShouldBe = 640.0;
    float heightShouldBe = widthShouldBe * ( self.size.height / self.size.width );
    
    CGSize size = CGSizeMake( widthShouldBe ,heightShouldBe );
    
    UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
    [result drawInRect:CGRectMake(0.0f, 0.0f, size.width, size.height)];
    result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return result;
    }

(obviously change the hard-coded output sizes as you wish.)

Note that your final result, will be achieved, by a combination in the appropriate order, such as:

yourImage = [yourImage doSquare];
yourImage = [yourImage doMask];

once you've got that working ...

Then ....

for LITERALLY what you ask, there are many example codes about .. e.g., what about https://stackoverflow.com/a/13870097/294884

As you can see, you fundamentally...

UIGraphicsBeginImageContextWithOptions(...);
UIBezierPath * path = [UIBezierPath
    bezierPathWithRoundedRect:imageRect cornerRadius:10.f];
[path addClip];
[yourImage drawInRect:imageRect];
... then ... UIGraphicsGetImageFromCurrentImageContext();
.. see the extensive code above for how to save it and so on.

You just have to get the zoom right with the scaling examples above.

Also note this, when you are changing the "area cropped"... https://stackoverflow.com/a/17884863/294884

here's an example of that critical technique...

-(UIImage *)squareAndSmall
    {
    // genius credits: https://stackoverflow.com/a/17884863/294884
    
    CGSize finalsize = CGSizeMake(640,640);
    
    CGFloat scale = MAX(
        finalsize.width/self.size.width,
        finalsize.height/self.size.height);
    CGFloat width = self.size.width * scale;
    CGFloat height = self.size.height * scale;
    
    // for example, the central area....
    CGRect imageRect = CGRectMake(
                  (finalsize.width - width)/2.0f,
                  (finalsize.height - height)/2.0f,
                  width, height);
    
    // or, the top area... CGRect imageRect = CGRectMake( 0, 0, width, height);
    
    UIGraphicsBeginImageContextWithOptions(finalsize, NO, 0);
    [self drawInRect:imageRect];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    
    UIGraphicsEndImageContext();
    return newImage;
    }

Hope it all helps!