How can I mask a UIImageView?

IosObjective CUiimageMask

Ios Problem Overview


I am trying to mask an image with something like this:

image to be masked

Would you please help me?

I am using this code:

- (void) viewDidLoad {
    UIImage *OrigImage = [UIImage imageNamed:@"dogs.png"];
    UIImage *mask = [UIImage imageNamed:@"mask.png"];
    UIImage *maskedImage = [self maskImage:OrigImage withMask:mask];
    myUIIMage.image = maskedImage;
}

Ios Solutions


Solution 1 - Ios

There's an easier way.

#import <QuartzCore/QuartzCore.h>
// remember to include Framework as well

CALayer *mask = [CALayer layer];
mask.contents = (id)[[UIImage imageNamed:@"mask.png"] CGImage];
mask.frame = CGRectMake(0, 0, <img_width>, <img_height>);
yourImageView.layer.mask = mask;
yourImageView.layer.masksToBounds = YES;

For Swift 4 and plus follow code below

let mask = CALayer()
mask.contents =  [ UIImage(named: "right_challenge_bg")?.cgImage] as Any
mask.frame = CGRect(x: 0, y: 0, width: leftBGImage.frame.size.width, height: leftBGImage.frame.size.height)
leftBGImage.layer.mask = mask
leftBGImage.layer.masksToBounds = true

Solution 2 - Ios

The tutorial uses this method with two parameters: image and maskImage, these you have to set when you call the method. An example call could look like this, assuming the method is in the same class and the pictures are in your bundle:

Note - amazingly the images do not even have to be the same size.

...
UIImage *image = [UIImage imageNamed:@"dogs.png"];
UIImage *mask = [UIImage imageNamed:@"mask.png"];

// result of the masking method
UIImage *maskedImage = [self maskImage:image withMask: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;
}

After you provided your code I have added some numbers as comments to it for reference. You still have two options. This whole thing is a method, which you are calling somewhere. You don't need to create the images inside it: this reduces the reusability of the method to zero.

To get your code working. Change the methods head (1.) to

- (UIImage *)maskImageMyImages {

Then change the name of the variable in 2. to

UIImage *maskImage = [UIImage imageNamed:@"mask.png"];
 

The method will return your masked images so you'll have to call this method in some place. Can you show us the code where you are calling your method?

Solution 3 - Ios

#Swift 3 - Simplest Solution I Found

I created an @IBDesignable for this so you could see the effect right away on your storyboard.

import UIKit

@IBDesignable
class UIImageViewWithMask: UIImageView {
    var maskImageView = UIImageView()
    
    @IBInspectable
    var maskImage: UIImage? {
        didSet {
            maskImageView.image = maskImage
            updateView()
        }
    }
    
    // This updates mask size when changing device orientation (portrait/landscape)
    override func layoutSubviews() {
        super.layoutSubviews()
        updateView()
    }
    
    func updateView() {
        if maskImageView.image != nil {
            maskImageView.frame = bounds
            mask = maskImageView
        }
    }
} 

##How to use

  1. Create a new file and paste in that code above.
  2. Add UIImageView to your storyboard (assign an image if you want).
  3. On Identity Inspector: Change the custom class to "UIImageViewWithMask" (custom class name above).
  4. On Attributes Inspector: Select mask image you want to use.

##Example Masking on Storyboard

##Notes

  • Your mask image should have a transparent background (PNG) for the non-black part.

Solution 4 - Ios

I tried both code using either CALayer or CGImageCreateWithMask but none of them didn't work for me

But i found out that the problem is with png file format, NOT the code!!
so just to share my finding!

If you want to use

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

you must use 24bit png without alpha channel

If you want to use CALayer mask, you must use (24bit or 8bit) png with alpha channel where transparent part of your png will mask the image (for smooth gradient alpha mask.. use 24bit png with alpha channel)

Solution 5 - Ios

- (UIImage*) maskImage:(UIImage *)image withMask:(UIImage *)maskImage {
		CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

		CGImageRef maskImageRef = [maskImage CGImage];

		// create a bitmap graphics context the size of the image
		CGContextRef mainViewContentContext = CGBitmapContextCreate (NULL, maskImage.size.width, maskImage.size.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);
		CGColorSpaceRelease(colorSpace);

		if (mainViewContentContext==NULL)
			return NULL;

		CGFloat ratio = 0;

		ratio = maskImage.size.width/ image.size.width;

		if(ratio * image.size.height < maskImage.size.height) {
			ratio = maskImage.size.height/ image.size.height;
		}

		CGRect rect1  = {{0, 0}, {maskImage.size.width, maskImage.size.height}};
		CGRect rect2  = {{-((image.size.width*ratio)-maskImage.size.width)/2 , -((image.size.height*ratio)-maskImage.size.height)/2}, {image.size.width*ratio, image.size.height*ratio}};


		CGContextClipToMask(mainViewContentContext, rect1, maskImageRef);
		CGContextDrawImage(mainViewContentContext, rect2, image.CGImage);


		// Create CGImageRef of the main view bitmap content, and then
		// release that bitmap context
		CGImageRef newImage = CGBitmapContextCreateImage(mainViewContentContext);
		CGContextRelease(mainViewContentContext);

		UIImage *theImage = [UIImage imageWithCGImage:newImage];

		CGImageRelease(newImage);

		// return the image
		return theImage;
}

This works for me.

Solution 6 - Ios

SWIFT 3 XCODE 8.1

func maskImage(image: UIImage, withMask maskImage: UIImage) -> UIImage {
    
    let maskRef = maskImage.cgImage
    
    let mask = CGImage(
        maskWidth: maskRef!.width,
        height: maskRef!.height,
        bitsPerComponent: maskRef!.bitsPerComponent,
        bitsPerPixel: maskRef!.bitsPerPixel,
        bytesPerRow: maskRef!.bytesPerRow,
        provider: maskRef!.dataProvider!,
        decode: nil,
        shouldInterpolate: false)
    
    let masked = image.cgImage!.masking(mask!)
    let maskedImage = UIImage(cgImage: masked!)
    
    // No need to release. Core Foundation objects are automatically memory managed.
    
    return maskedImage
    
}

// for use

testImage.image = maskImage(image: UIImage(named: "OriginalImage")!, withMask: UIImage(named: "maskImage")!)

Solution 7 - Ios

Swift version of the accepted solution:

func maskImage(image: UIImage, withMask maskImage: UIImage) -> UIImage {

    let maskRef = maskImage.CGImage

    let mask = CGImageMaskCreate(
        CGImageGetWidth(maskRef),
        CGImageGetHeight(maskRef),
        CGImageGetBitsPerComponent(maskRef),
        CGImageGetBitsPerPixel(maskRef),
        CGImageGetBytesPerRow(maskRef),
        CGImageGetDataProvider(maskRef),
        nil,
        false)

    let masked = CGImageCreateWithMask(image.CGImage, mask)
    let maskedImage = UIImage(CGImage: masked)!

    // No need to release. Core Foundation objects are automatically memory managed.

    return maskedImage

}

Just in case someone need it.

It`s working for me flawlessly.

Solution 8 - Ios

we found, that correct answer is not working now and implemented little drop-in class, which helps to mask any image in UIImageView.

It's available here: https://github.com/Werbary/WBMaskedImageView

Example:

WBMaskedImageView *imgView = [[WBMaskedImageView alloc] initWithFrame:frame];
imgView.originalImage = [UIImage imageNamed:@"original_image.png"];
imgView.maskImage = [UIImage imageNamed:@"mask_image.png"];

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioniOS.LoverView Question on Stackoverflow
Solution 1 - IosBartosz CiechanowskiView Answer on Stackoverflow
Solution 2 - IosNick WeaverView Answer on Stackoverflow
Solution 3 - IosMark MoeykensView Answer on Stackoverflow
Solution 4 - IosNinjiView Answer on Stackoverflow
Solution 5 - IosZYiOSView Answer on Stackoverflow
Solution 6 - IosAhmed SafadiView Answer on Stackoverflow
Solution 7 - IosJuan ValeraView Answer on Stackoverflow
Solution 8 - IoswerbaryView Answer on Stackoverflow