Search code examples
iosiphoneobjective-cxcodexcode5

how to do hexagon type image masking on imageview in ios?


i want to implement image masking on image view in iOS. image masking should be hexagon with round corners. some of the third party code are available but they don't have round corners.

please help me. as i am new in iOS.


Solution

  • Depending on how you're showing the image you can mask it a few different ways.

    Case 1: CALayer

    You can use the mask property on the CALayer. This property is of CALayer* type and will mask the target layer appropriately to the alpha channel of the given layer. Note this property can be a subclass of CALayer, notably CAShapeLayer which allows you to specify a direct path for the mask (here you can specify the hexagon). Also note to make sure you set some opaque color on the masked layer in order for the masked layer to actually become masked.

    Note: If you need help drawing the path of the hexagon let me know, I recently did the geometry for that earlier today :D

    Case 2: CoreGraphics

    You can use the CGImageMaskCreate and CGImageCreateWithMask like so:

    - (UIImage *)maskImage:(UIImage *)image withMask:(UIImage *)mask {
    
      CGImageRef cgmask = CGImageMaskCreate(CGImageGetWidth(mask.CGImage), CGImageGetHeight(mask.CGImage), CGImageGetBitsPerComponent(mask.CGImage), CGImageGetBitsPerPixel(mask.CGImage), CGImageGetBytesPerRow(mask.CGImage), CGImageGetDataProvider(mask.CGImage), NULL, false);
      CGImageRef cgmaskedImage = CGImageCreateWithMask(image.CGImage, cgmask);
      UIImage *retval = [UIImage imageWithCGImage:maskedImageRef];
      CGImageRelease(cgmask);
      CGImageRelease(cgmaskedImage);
    
      return retval;
    }
    

    The mask image is supposed to be black where you want the mask to be. This way you could create a hexagon in photoshop and then use this method to create a masked image. What's nice is this will automatically scale the mask image if need be also.

    I personally prefer using CALayers since even a UIImageView is backed by a CALayer and I'm fairly familiar with CALayers. That said I'm unsure which is more performant or the correct way to do this.

    EDIT: Adding hexagon drawing code.

    Here I will create some code to create a CGPathRef which will represent a hexagon.

      UIView *v = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
      v.backgroundColor = [UIColor purpleColor];
    
      CGRect rect = v.frame;
    
      CAShapeLayer *hexagonMask = [CAShapeLayer layer];
      UIBezierPath *hexagonPath = [UIBezierPath bezierPath];
      CGFloat sideWidth = 2 * ( 0.5 * rect.size.width / 2 );
      CGFloat lcolumn = ( rect.size.width - sideWidth ) / 2;
      CGFloat rcolumn = rect.size.width - lcolumn;
      CGFloat height = 0.866025 * rect.size.height;
      CGFloat y = (rect.size.height - height) / 2;
      CGFloat by = rect.size.height - y;
      CGFloat midy = rect.size.height / 2;
      CGFloat rightmost = rect.size.width;
      [hexagonPath moveToPoint:CGPointMake(lcolumn, y)];
      [hexagonPath addLineToPoint:CGPointMake(rcolumn, y)];
      [hexagonPath addLineToPoint:CGPointMake(rightmost, midy)];
      [hexagonPath addLineToPoint:CGPointMake(rcolumn, by)];
      [hexagonPath addLineToPoint:CGPointMake(lcolumn, by)];
      [hexagonPath addLineToPoint:CGPointMake(0, midy)];
      [hexagonPath addLineToPoint:CGPointMake(lcolumn, y)];
    
      hexagonMask.path = hexagonPath.CGPath;
      v.layer.mask = hexagonMask;
    

    You can substitute your image view in for v and it should automatically mask it.

    EDIT: Code listing for hexagon with border. Note that the lineWidth in this example will only appear to be 2.5 since half of it will be clipped. If you want a real 5 pixel border then specify the lineWidth to be 10.

      UIView *v = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
      v.backgroundColor = [UIColor purpleColor];
    
      CGRect rect = v.frame;
    
      CAShapeLayer *hexagonMask = [CAShapeLayer layer];
      CAShapeLayer *hexagonBorder = [CAShapeLayer layer];
      hexagonBorder.frame = v.layer.bounds;
      UIBezierPath *hexagonPath = [UIBezierPath bezierPath];
      CGFloat sideWidth = 2 * ( 0.5 * rect.size.width / 2 );
      CGFloat lcolumn = ( rect.size.width - sideWidth ) / 2;
      CGFloat rcolumn = rect.size.width - lcolumn;
      CGFloat height = 0.866025 * rect.size.height;
      CGFloat y = (rect.size.height - height) / 2;
      CGFloat by = rect.size.height - y;
      CGFloat midy = rect.size.height / 2;
      CGFloat rightmost = rect.size.width;
      [hexagonPath moveToPoint:CGPointMake(lcolumn, y)];
      [hexagonPath addLineToPoint:CGPointMake(rcolumn, y)];
      [hexagonPath addLineToPoint:CGPointMake(rightmost, midy)];
      [hexagonPath addLineToPoint:CGPointMake(rcolumn, by)];
      [hexagonPath addLineToPoint:CGPointMake(lcolumn, by)];
      [hexagonPath addLineToPoint:CGPointMake(0, midy)];
      [hexagonPath addLineToPoint:CGPointMake(lcolumn, y)];
    
      hexagonMask.path = hexagonPath.CGPath;
      hexagonBorder.path = hexagonPath.CGPath;
      hexagonBorder.fillColor = [UIColor clearColor].CGColor;
      hexagonBorder.strokeColor = [UIColor blackColor].CGColor;
      hexagonBorder.lineWidth = 5;
      v.layer.mask = hexagonMask;
      [v.layer addSublayer:hexagonBorder];