Como posso mascarar um UIImageView?


100

Estou tentando mascarar uma imagem com algo assim:

imagem a ser mascarada

Você poderia me ajudar?

Estou usando este código:

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

27
Eu sou a pessoa que escreveu o tutorial original. A imagem da máscara é apenas uma imagem simples em tons de cinza que criei no photoshop. Nada de especial nisso. A área preta se torna a parte "transparente" da máscara. Lembre-se de que qualquer tom de cinza é interpretado como um grau de opacidade. Dessa forma, as máscaras também podem ser gradientes, o que é útil para criar bordas mais suaves ao redor de uma máscara.
— ra9r

Isso tem que ser do mesmo tamanho, certo?
— KarenAnne

código hiperelegante, obrigado. apenas um link útil se alguém precisar cortar uma imagem antes de mascarar ... stackoverflow.com/questions/17712797/…
— Fattie


1
Peço que altere a resposta selecionada para aquela com quase 3 vezes mais votos positivos. Na minha opinião, é melhor em quase todos os sentidos.
— Albert Renshaw de

Respostas:


163

Existe uma maneira mais fácil.

#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;

Para Swift 4 e mais, siga o código abaixo

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
obrigado, mas eu coloquei esse código, viewDidLoadmas nada aconteceu!
— Mc.Lover

6
Você deve definir a propriedade da camada para usar a máscara, ou seja, [yourImageView.layer setMasksToBounds: YES];
— Wolfgang Schreurs

3
Observe que é necessário ser um UIImageView para esta solução. A outra solução funciona apenas com um UIImage.
— adriendenat

1
Tentei essa abordagem, mas parece que não funciona no iOS 8
— bdv

2
Para código Swift, quando colocado em mask.contents, deve CGImage. Não [CGImage]. Se usar colchete [], significa Array.
— strawnut

59

O tutorial usa este método com dois parâmetros: imagee maskImage, estes você deve definir ao chamar o método. Um exemplo de chamada poderia ser assim, supondo que o método esteja na mesma classe e as imagens estejam em seu pacote:

Observação - surpreendentemente, as imagens nem precisam ter o mesmo tamanho.

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

Depois que você forneceu seu código, adicionei alguns números como comentários para referência. Você ainda tem duas opções. Tudo isso é um método, que você está chamando em algum lugar. Você não precisa criar as imagens dentro dele: isso reduz a reutilização do método a zero.

Para fazer seu código funcionar. Altere os métodos head ( 1. ) para

- (UIImage *)maskImageMyImages {

Em seguida, altere o nome da variável em 2. para

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

O método retornará suas imagens mascaradas, então você terá que chamar este método em algum lugar. Você pode nos mostrar o código onde está chamando seu método?


Desculpe ! onde deve escrever o UIImage *imagecódigo? !!! porque no -(UIimage *) maskeImagecompilador dá erro de redefinição!
— Mc.Lover

desculpe, você poderia enviar um código de amostra para mim? Estou confuso: -s
— Mc.Lover

1
Não é possível, está tudo aí. Você terá que nos mostrar como está chamando este método, ou pelo menos a parte em que gostaria de mascarar suas imagens e mostrar o resultado.
— Nick Weaver

meu problema é chamá-lo, deveria ser algo assim ?? (viewDidLoad): veja meu código editado
— Mc.Lover

Qualquer ideia sobre o Mascaramento reverso. (Processo reverso do acima). Significa que eu quero remover a área de preenchimento (torná-la transparente) e área transparente com preenchimento.
— Milan Patel

16

Swift 3 - Solução mais simples que encontrei

Criei um @IBDesignablepara isso para que você pudesse ver o efeito imediatamente no seu 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
        }
    }
} 

Como usar

  1. Crie um novo arquivo e cole o código acima.
  2. Adicione UIImageView ao seu storyboard (atribua uma imagem se quiser).
  3. No Identity Inspector: Altere a classe personalizada para "UIImageViewWithMask" (nome da classe personalizada acima).
  4. No Attributes Inspector: Selecione a imagem de máscara que deseja usar.

Exemplo

Mascaramento no Storyboard

Notas

  • Sua imagem de máscara deve ter um fundo transparente (PNG) para a parte não preta.

12

Eu tentei os dois códigos usando CALayer ou CGImageCreateWithMask, mas nenhum deles não funcionou para mim

Mas eu descobri que o problema é com o formato do arquivo png, NÃO com o código !!
então, apenas para compartilhar minha descoberta!

Se você quiser usar

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

você deve usar 24 bits png sem canal alfa

Se você quiser usar a máscara CALayer, você deve usar (24 bits ou 8 bits) png com canal alfa onde parte transparente de seu png irá mascarar a imagem (para máscara alfa de gradiente suave .. use 24 bits png com canal alfa)


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

Isso funciona para mim.


obrigado, essa resposta é melhor do que outras falando sobre performance com mais e maiores imagens.
— Radu Ursache

1
Alguma ideia sobre o mascaramento reverso. (Processo reverso do processo acima). Significa que eu quero remover a área de preenchimento (torná-la transparente) e a área Transparente com preenchimento.
— Milan Patel

Obrigado, este código funciona melhor do que o primeiro !! Mas eu tenho dois problemas: Aviso sobre "kCGImageAlphaPremultipliedLast" (eu transmito para "CGBitmapInfo" para corrigi-lo) E não funciona para imagens de retina !! você pode me ajudar ?? ...
— fingerup

@Milanpatel A solução fácil é simplesmente inverter suas cores na imagem da máscara. :)
— Fez

@ZyiOS como posso personalizar o quadro da imagem mascarada. ou seja, o usuário pode aumentar ou diminuir o tamanho da imagem mascarada.
— Dalvik,

8

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

}

// para uso

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

você pode me dar a foto
— Ahmed Safadi

3

Versão rápida da solução aceita:

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

}

Apenas no caso de alguém precisar.

Está funcionando para mim perfeitamente.


Você tem uma versão Swift 3.0 disso?
— Van Du Tran

2

descobrimos que a resposta correta não está funcionando agora e implementamos uma pequena classe drop-in, o que ajuda a mascarar qualquer imagem em UIImageView.

Está disponível aqui: https://github.com/Werbary/WBMaskedImageView

Exemplo:

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

1
Um bom! Realmente ajudou.
— abhimuralidharan
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.