8

Can we set below shape on image with UIImageView.

Any idea!

Thanks

enter image description here

Ajith Renjala
  • 4,934
  • 5
  • 34
  • 42
kb920
  • 3,039
  • 2
  • 33
  • 44

5 Answers5

17

Swift 3 Version - With Border

Like others suggest, I would use the image's mask property.

Info from Apple about the mask property:

The layer’s alpha channel determines how much of the layer’s content and background shows through. Fully or partially opaque pixels allow the underlying content to show through but fully transparent pixels block that content. The default value of this property is nil nil.

When configuring a mask, remember to set the size and position of the mask layer to ensure it is aligned properly with the layer it masks.

That last sentence is important!

1. Create Your Mask

I used Sketch to create a black image with a transparent background (PNG).

Add it to your Assets.xcassets folder. My image is called profileMask.

Mask Image

2. Add UIImageView to Storyboard

Create an outlet for the image view. I called mine profileImageView.

Storyboard

3. Code viewDidLoad

override func viewDidLoad() {
    super.viewDidLoad()

    let maskImageView = UIImageView()
    maskImageView.contentMode = .scaleAspectFit
    maskImageView.image = #imageLiteral(resourceName: "profileMask")
    maskImageView.frame = profileImageView.bounds
    profileImageView.mask = maskImageView
}

Note:

  • I added the contentMode because without it my mask was stretching to match the UIImageView's bounds (width). You can solve this by just making your UIImageView the same size as your mask image too or vice versa.

Here is what it looks like when run: In Simulator

What About The Border?

Guys, this was a pain to do. I tried to create a Bezier Path but to be honest my skills on making an exact shape to match the mask was impossible for me. A Bezier Path would probably look really good if you have the skills to do that but I don't. :(

So I had the idea, "What if I just use the mask image as the border?" It worked! The benefit of doing it this way is you can use ANY mask shape in the future and it will create a border for it without having to figure out the bezier path.

Here's how I did it:

  1. In the Assets.xcassets, select your mask and change this Render As property to "Template Image". This allows you to change the color of the mask image by changing the UIImageView's Tint Color property Template Image
  2. Then in the code I set the profileImageView to the mask and change the tint color to whatever I want the border to be.
  3. Then I created another image view for Jason's profile picture and applied the mask to it. I made it the same size as profileImageView minus 2 points (to show the underlying mask image which will be the border).
  4. Last step is to them add the masked image as a subview of profileImageView.

Here's the code in viewDidLoad now:

override func viewDidLoad() {
    super.viewDidLoad()

    // Add border first (mask image)
    profileImageView.contentMode = .scaleAspectFit
    profileImageView.tintColor = .lightGray // Border Color
    profileImageView.image = #imageLiteral(resourceName: "profileMask")

    // Profile Image
    let profileImage = UIImageView()
    profileImage.image = #imageLiteral(resourceName: "Profile")
    profileImage.contentMode = .scaleAspectFill
    // Make a little bit smaller to show "border" image behind it
    profileImage.frame = profileImageView.bounds.insetBy(dx: 2, dy: 2)

    // Mask Image
    let maskImageView = UIImageView()
    maskImageView.image = #imageLiteral(resourceName: "profileMask")
    maskImageView.contentMode = .scaleAspectFit
    maskImageView.frame = profileImage.bounds

    // Apply mask to profile iamge
    profileImage.mask = maskImageView

    // Add profile image as a subview on top of the "border" image
    profileImageView.addSubview(profileImage)
}

So now it looks like this when we run it:

With Border

Closing

What could be really cool is if we create a custom UIImageView with a mask and border width property. Then you can just assign these properties and the custom class will take care of the rest. Let me know if you guys want to see something like this and I'll create it and post it here. Maybe also make a YouTube video tutorial for my channel.

Mark Moeykens
  • 15,915
  • 6
  • 63
  • 62
9

Mask UIImageView

You can use mask property of imageView's CALayer:

CALayer *mask = [CALayer layer];
mask.contents = (id)[[UIImage imageNamed:@"mask.png"] CGImage];
mask.frame = CGRectMake(0, 0, imageView.image.size.width, imageView.image.size.height);
imageView.layer.mask = mask;
imageView.layer.masksToBounds = YES;

Apply Core Graphics mask to UIImage

This will apply to ImageView only, so if you want to change UIImage directly - you should use Core Graphics mask:

- (UIImage*) maskImage:(UIImage *)image withMask:(UIImage *)maskImage {

    CGImageRef maskRef = maskImage.CGImage;

    CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
                                        CGImageGetHeight(maskRef),
                                        CGImageGetBitsPerComponent(maskRef),
                                        CGImageGetBitsPerPixel(maskRef),
                                        CGImageGetBytesPerRow(maskRef),
                                        CGImageGetDataProvider(maskRef), NULL, false);

    CGImageRef maskedImageRef = CGImageCreateWithMask([image CGImage], mask);
    UIImage *maskedImage = [UIImage imageWithCGImage:maskedImageRef];

    CGImageRelease(mask);
    CGImageRelease(maskedImageRef);

    // returns new image with mask applied
    return maskedImage;
}

Core Graphics image composition with blending

Swift 5.

let image = UIImage(named: "Lenna.png")!
let mask = UIImage(named: "mask-image.png")! // background is transparent
let size = image.size

let renderer = UIGraphicsImageRenderer(size: size)
let newImage = renderer.image { context in
    image.draw(in: CGRect(origin: .zero, size: size), blendMode: .normal, alpha: 1)
    mask.draw(in: CGRect(origin: .zero, size: size), blendMode: .destinationIn, alpha: 1)
}

Playground:

Blending example playground

Evgeny Aleksandrov
  • 780
  • 14
  • 32
  • This only works with a white background , not transparent. How would one make this work with a transparent background instead? – FreeAppl3 Jan 28 '20 at 11:50
  • @FreeAppl3 what do you mean by "don't work"? It doesn't apply mask? Or it has wrong background after applying? Also this answer has 2 solutions, have you tried both of them? – Evgeny Aleksandrov Jan 28 '20 at 15:20
  • @FreeAppl3 It would be best to create new question with your code and example images there. – Evgeny Aleksandrov Jan 28 '20 at 15:21
  • I never said it "don't work" not sure what you mean by that, but yes it doesn't apply the mask if the masks background is transparent and that is what the initial question is using. This method works with a black area for mask and white background. Did not want to duplicate question. – FreeAppl3 Jan 29 '20 at 02:21
  • Thanks for clarification. Check this answer - https://stackoverflow.com/a/14083491/840588. Looks like `CGImageCreateWithMask` doesn't work with alpha channel. – Evgeny Aleksandrov Jan 29 '20 at 12:11
  • 1
    @FreeAppl3 Check updated answer. I've added solution for transparent background blending (if I understood you correctly). – Evgeny Aleksandrov Jan 29 '20 at 13:13
6

Yes, you can set a shape on UIImage. First, add a Mask image(which shape you want) in your project. and write these code.

[yourImageView setImage:[self createMaskImage:yourImage withMask:MaskImage]];

- (UIImage*) createMaskImage:(UIImage *)image withMask:(UIImage *)maskImage {
    CGImageRef maskRef = maskImage.CGImage;

    CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
                                        CGImageGetHeight(maskRef),
                                        CGImageGetBitsPerComponent(maskRef),
                                        CGImageGetBitsPerPixel(maskRef),
                                        CGImageGetBytesPerRow(maskRef),
                                        CGImageGetDataProvider(maskRef), NULL, false);

    CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);
    return [UIImage imageWithCGImage:masked];
}
Evgeny Aleksandrov
  • 780
  • 14
  • 32
Monikanta
  • 307
  • 2
  • 8
  • I don't know why but his same code is not work for me. – Crazy Developer Aug 31 '16 at 07:30
  • i don't know how to possible image centre part curve shape like slim so any one know this concept please let me know . my project main image view image some selected part like body shape slim any one know please guy help for this concept objective c – Ramani Hitesh Mar 27 '18 at 13:43
1

Sorry for hardcode. Try to use UIBezierPath to set any shape you want.

CAShapeLayer* maskLayer = [CAShapeLayer layer];
UIBezierPath* path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(75, 200)];
[path addCurveToPoint:CGPointMake(0, 100) controlPoint1:CGPointMake(0, 150) controlPoint2:CGPointMake(0, 150)];
[path addArcWithCenter:CGPointMake(75, 100) radius:75 startAngle:M_PI endAngle:0 clockwise:YES];
[path addCurveToPoint:CGPointMake(75, 200) controlPoint1:CGPointMake(150, 150) controlPoint2:CGPointMake(150, 150)];
[path closePath];
maskLayer.backgroundColor = [[UIColor clearColor] CGColor];
maskLayer.path = [path CGPath];

yourImageView.layer.mask = maskLayer;
Evgeny Aleksandrov
  • 780
  • 14
  • 32
Valentin Shamardin
  • 3,569
  • 4
  • 34
  • 51
1

It's easy by using

UIImageView *image=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"balloon_selected_left.png"] ];
    self.maskImage.layer.mask=image.layer;
Saurabh Jain
  • 1,688
  • 14
  • 28