Comment puis-je masquer une UIImageView?

100

J'essaye de masquer une image avec quelque chose comme ceci:

image à masquer

Pourriez vous m'aider s'il vous plait?

J'utilise ce 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;
}
Mc.Lover
la source
27
Je suis la personne qui a écrit le tutoriel original. L'image du masque n'est qu'une simple image en niveaux de gris que j'ai créée dans Photoshop. Rien de spécial à ce sujet. La zone noire devient la partie "transparente" du masque. Gardez à l'esprit que toute nuance de gris est interprétée comme un degré d'opacité. De cette manière, les masques peuvent également être des dégradés, ce qui est utile pour créer des bordures plus douces autour d'un masque.
ra9r
Cela doit être de la même taille, non?
KarenAnne
code hyper élégant, merci. juste un lien utile si quelqu'un a besoin de recadrer une image avant de la masquer ... stackoverflow.com/questions/17712797
Fattie
Une idée à ce sujet .. stackoverflow.com/questions/28122370/…
Milan patel
1
Je vous exhorte à changer la réponse sélectionnée par celle avec près de 3 fois plus de votes positifs. À mon avis, c'est mieux dans presque tous les sens.
Albert Renshaw

Réponses:

165

Il existe un moyen plus simple.

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

Pour Swift 4 et plus, suivez le code ci-dessous

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
Bartosz Ciechanowski
la source
1
merci mais j'ai mis ce code viewDidLoadmais rien ne s'est passé!
Mc.Lover
6
Vous devez définir la propriété layer pour utiliser le masque, c'est-à-dire [yourImageView.layer setMasksToBounds: YES];
Wolfgang Schreurs
3
Notez que cela est nécessaire pour être une UIImageView pour cette solution. L'autre solution fonctionne uniquement avec une UIImage.
adriendenat
1
J'ai essayé cette approche mais cela ne semble pas fonctionner dans iOS 8
bdv
2
Pour le code Swift, lorsqu'il est placé dans mask.contents, CGImage. Pas [CGImage]. Si vous utilisez le crochet [], cela signifie Array.
fraise du
59

Le didacticiel utilise cette méthode avec deux paramètres: imageet maskImage, vous devez les définir lorsque vous appelez la méthode. Un exemple d'appel pourrait ressembler à ceci, en supposant que la méthode est dans la même classe et que les images sont dans votre bundle:

Remarque - étonnamment, les images n'ont même pas besoin d'être de la même taille.

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

Après avoir fourni votre code, j'ai ajouté quelques chiffres en tant que commentaires à titre de référence. Vous avez encore deux options. Tout cela est une méthode que vous appelez quelque part. Vous n'avez pas besoin de créer les images à l'intérieur: cela réduit la réutilisabilité de la méthode à zéro.

Pour faire fonctionner votre code. Changez la tête des méthodes ( 1. ) en

- (UIImage *)maskImageMyImages {

Puis changez le nom de la variable en 2. en

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

La méthode renverra vos images masquées, vous devrez donc appeler cette méthode à un endroit. Pouvez-vous nous montrer le code où vous appelez votre méthode?

Nick Weaver
la source
Désolé ! où doit écrire le UIImage *imagecode? !!! car sur le -(UIimage *) maskeImagecompilateur donne une erreur de redéfinition!
Mc.Lover
désolé, veuillez télécharger un exemple de code pour moi?
Je suis
1
Pas possible, tout y est. Vous devrez nous montrer comment vous appelez cette méthode, ou du moins la partie dans laquelle vous souhaitez masquer vos images et afficher le résultat.
Nick Weaver
mon problème est de l'appeler, est-ce que ça devrait être quelque chose comme ça ?? (viewDidLoad): voir mon code édité
Mc.Lover
Toute idée sur le masquage inversé. (Inverse du processus ci-dessus) signifie que je veux supprimer la zone de remplissage (la rendre transparente) et la zone transparente avec remplissage.
Milan patel
17

Swift 3 - La solution la plus simple que j'ai trouvée

J'ai créé un @IBDesignablepour cela afin que vous puissiez voir l'effet tout de suite sur votre 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
        }
    }
} 

Comment utiliser

  1. Créez un nouveau fichier et collez ce code ci-dessus.
  2. Ajoutez UIImageView à votre storyboard (attribuez une image si vous le souhaitez).
  3. Sur l'inspecteur d'identité: remplacez la classe personnalisée par "UIImageViewWithMask" (nom de classe personnalisé ci-dessus).
  4. Sur l'inspecteur d'attributs: sélectionnez l'image de masque que vous souhaitez utiliser.

Exemple

Masquage sur Storyboard

Remarques

  • L'image de votre masque doit avoir un arrière-plan transparent (PNG) pour la partie non noire.
Mark Moeykens
la source
12

J'ai essayé les deux codes en utilisant CALayer ou CGImageCreateWithMask mais aucun d'entre eux n'a fonctionné pour moi

Mais j'ai découvert que le problème venait du format de fichier png, PAS du code !!
donc juste pour partager ma découverte!

Si vous souhaitez utiliser

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

vous devez utiliser png 24 bits sans canal alpha

Si vous souhaitez utiliser le masque CALayer, vous devez utiliser (24 bits ou 8 bits) png avec canal alpha où une partie transparente de votre png masquera l'image (pour un masque alpha dégradé lisse .. utilisez png 24 bits avec canal alpha)

Ninji
la source
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;
}

Cela fonctionne pour moi.

ZYiOS
la source
merci, cette réponse est meilleure que celle des autres parlant de performance avec des images de plus en plus grandes.
Radu Ursache
1
Toute idée sur le masquage inversé. (Inverse du processus ci-dessus) signifie que je veux supprimer la zone de remplissage (la rendre transparente) et la zone transparente avec remplissage.
Milan patel
Merci, ce code fonctionne mieux que le premier !! Mais j'ai deux problèmes: Avertissement sur "kCGImageAlphaPremultipliedLast" (j'ai casté vers "CGBitmapInfo" pour le réparer) ET ne fonctionne pas pour les images rétine !! pouvez-vous m'aider ?? ...
fingerup
@Milanpatel La solution simple consiste simplement à inverser vos couleurs dans l'image du masque. :)
Dids
@ZyiOS comment puis-je personnaliser le cadre de l'image masquée. c'est-à-dire que l'utilisateur peut augmenter ou diminuer la taille de l'image masquée.
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

}

// pour utilisation

testImage.image = maskImage(image: UIImage(named: "OriginalImage")!, withMask: UIImage(named: "maskImage")!)
Ahmed Safadi
la source
pouvez-vous me donner l'image
Ahmed Safadi
3

Version Swift de la solution acceptée:

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

}

Juste au cas où quelqu'un en aurait besoin.

Cela fonctionne parfaitement pour moi.

Juan Valera
la source
En avez-vous une version Swift 3.0?
Van Du Tran
2

nous avons trouvé que la bonne réponse ne fonctionne pas maintenant et implémenté une petite classe de remplacement, qui permet de masquer toute image dans UIImageView.

Il est disponible ici: https://github.com/Werbary/WBMaskedImageView

Exemple:

WBMaskedImageView *imgView = [[WBMaskedImageView alloc] initWithFrame:frame];
imgView.originalImage = [UIImage imageNamed:@"original_image.png"];
imgView.maskImage = [UIImage imageNamed:@"mask_image.png"];
werbary
la source
1
Bon! Cela a vraiment aidé.
abhimuralidharan