如何对UIImageView进行遮罩处理?

108

我正在尝试像这样使用遮罩遮盖一个图像:

image to be masked

请问你能帮我吗?

我正在使用以下代码:

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

28
我是写原始教程的人。口罩图像只是我在Photoshop中创建的简单灰度图像,没有什么特别之处。黑色区域成为口罩的“透明”部分。请记住,任何灰度都被解释为不透明度的程度。通过这种方式,口罩也可以是渐变的,这对于在口罩周围创建更柔和的边框很有用。 - ra9r
这个必须大小相同,对吧? - KarenAnne
超级优雅的代码,谢谢。如果有人需要在遮罩之前裁剪图像,这是一个有用的链接...https://dev59.com/x2Mm5IYBdhLWcg3wT9mU - Fattie
有关这个的任何想法吗?https://dev59.com/e4fca4cB1Zd3GeqPfjTh - Milan patel
1
我敦促您将所选答案更改为获得近三倍赞同票数的答案。在我看来,它在几乎所有方面都更好。 - Albert Renshaw
8个回答

174
有一种更简单的方法。
#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, yourImageView.frame.size.width, yourImageView.frame.size.height);
yourImageView.layer.mask = mask;
yourImageView.layer.masksToBounds = YES;

对于Swift 4及以上版本,请按照以下代码进行操作。
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

1
谢谢你,但我把这段代码放在viewDidLoad中,但什么也没有发生! - iOS.Lover
6
您应该设置图层属性来使用遮罩,即**[yourImageView.layer setMasksToBounds:YES];**。 - Wolfgang Schreurs
3
请注意,这对于此解决方案来说是必须的,需要一个UIImageView。另一种解决方案仅适用于UIImage。 - adriendenat
1
这不仅是更简单的方式,也是正确的做法!这应该成为被接受的答案。 - sabalaba
6
在Swift代码中,当放入mask.contents时,应该使用CGImage而不是[CGImage]。如果使用方括号[],则意味着数组(Array)。 - strawnut
显示剩余8条评论

61

这个教程使用带有两个参数的方法:imagemaskImage,当你调用该方法时必须设置这些参数。例如,如果该方法在同一个类中且图片在你的捆绑包中,则调用示例可能如下:

注意 - 令人惊讶的是,这些图片甚至不必是相同大小的。

...
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;
}

在您提供了代码之后,我已经添加了一些数字作为参考注释。您仍然有两个选择。整个东西是一个方法,在某个地方进行调用。您不需要在其中创建图像:这将使该方法的可重用性降为零。

要使您的代码运行,请更改方法头(1.)为

- (UIImage *)maskImageMyImages {

然后将2.中的变量名改为:

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

这个方法会返回经过遮罩处理的图像,因此您需要在某个位置调用该方法。 您能否展示一下您调用该方法的代码?


抱歉!在哪里应该写UIImage *image的代码?因为在-(UIimage *) maskeImage中编译器会报重定义错误! - iOS.Lover
对不起,您能否为我上传一份示例代码?我有点困惑。:-s - iOS.Lover
1
不可能,一切都在那里。你必须向我们展示你是如何调用这个方法的,或者至少展示你想要遮盖图像并显示结果的部分。 - Nick Weaver
我的问题是如何调用它,应该像这样吗?(viewDidLoad):请查看我的编辑代码。 - iOS.Lover
你知道反向遮罩的实现方法吗?这意味着我想要去除填充区域(使其透明),并用填充替换透明区域。 - Milan patel
@NickWeaver 你能否得到一个像边框的狗一样的结果图像。意味着将掩膜设置为掩膜图像的白色部分,而不是清晰部分。 - Pramod Tapaniya

19

Swift 3 - 我找到的最简单方案

我创建了一个@IBDesignable,这样您就可以立即在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
        }
    }
} 

如何使用

  1. 创建一个新文件并将上述代码粘贴到其中。
  2. 在Storyboard中添加UIImageView(如果需要,分配一张图片)。
  3. 在“Identity Inspector”中:将自定义类更改为“UIImageViewWithMask”(如上所示)。
  4. 在“Attributes Inspector”中:选择要使用的遮罩图像。

示例

在Storyboard上使用遮罩

注意事项

  • 您的遮罩图像应具有透明背景(PNG格式),以便非黑色部分可以显示出来。

12

我尝试了使用CALayer和CGImageCreateWithMask两种代码,但都没有对我起作用

但是我发现问题在于PNG文件格式,而不是代码!! 所以只是分享我的发现!

如果你想使用

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

你必须使用没有 alpha 通道的 24 位 PNG。

如果你想要使用 CALayer mask,你必须使用 (24 位或 8 位) 带有 alpha 通道的 PNG,其中 PNG 的透明部分将作为遮罩来遮盖图像(对于平滑渐变 alpha mask,请使用带有 alpha 通道的 24 位 PNG)。


10
- (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;
}

这对我起作用。


谢谢,这个答案比其他的更好,讨论了性能问题,并且使用了更多和更大的图片。 - Radu Ursache
1
有关反向遮罩的任何想法(与上述过程相反)。这意味着我想要删除填充区域(使其透明)并用填充替换透明区域。 - Milan patel
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - fingerup
@Milanpatel 简单的解决方案是在掩膜图像中简单地反转颜色。 :) - Dids
@ZyiOS,我该如何自定义遮罩图像的框架?即用户可以增加或减小遮罩图像的大小。 - Rahul

9

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

}

// 供使用

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

你能给我这张图片吗? - Ahmed Safadi

3

这是已经被接受的解决方案的Swift版本:

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

}

万一有人需要的话。

对我来说,它完美地正常工作。


你有这个的Swift 3.0版本吗? - Van Du Tran

2
我们发现正确答案现在不起作用了,因此实现了一个小的插件类,可以帮助遮盖UIImageView中的任何图像。
这里提供下载链接:https://github.com/Werbary/WBMaskedImageView 示例:
WBMaskedImageView *imgView = [[WBMaskedImageView alloc] initWithFrame:frame];
imgView.originalImage = [UIImage imageNamed:@"original_image.png"];
imgView.maskImage = [UIImage imageNamed:@"mask_image.png"];

1
不错!它真的很有帮助。 - abhimuralidharan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接