Wie kann ich eine UIImageView maskieren?

100

Ich versuche ein Bild mit so etwas zu maskieren:

zu maskierendes Bild

Würden Sie mir bitte helfen?

Ich benutze diesen 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
quelle
27
Ich bin die Person, die das ursprüngliche Tutorial geschrieben hat. Das Maskenbild ist nur ein einfaches Graustufenbild, das ich in Photoshop erstellt habe. Nichts Besonderes. Der schwarze Bereich wird zum "transparenten" Teil der Maske. Beachten Sie, dass jeder Grauton als Grad an Deckkraft interpretiert wird. Auf diese Weise können Masken auch Farbverläufe sein, was nützlich ist, um weichere Ränder um eine Maske zu erstellen.
ra9r
Das muss gleich groß sein, oder?
KarenAnne
hypereleganter Code, danke. Nur ein nützlicher Link, wenn jemand vor dem Maskieren ein Bild zuschneiden muss
Fattie
Irgendeine Idee dazu .. stackoverflow.com/questions/28122370/…
Milan patel
1
Ich fordere Sie auf, die ausgewählte Antwort in eine Antwort mit fast dreimal so vielen positiven Stimmen zu ändern. Meiner Meinung nach ist es in fast jeder Hinsicht besser.
Albert Renshaw

Antworten:

163

Es gibt einen einfacheren Weg.

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

Für Swift 4 und höher folgen Sie dem Code unten

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
quelle
1
Danke, aber ich habe diesen Code angelegt, viewDidLoadaber nichts ist passiert!
Mc.Lover
6
Sie sollten die Layer-Eigenschaft so festlegen, dass die Maske verwendet wird, dh [yourImageView.layer setMasksToBounds: YES];
Wolfgang Schreurs
3
Beachten Sie, dass dies erforderlich ist, um eine UIImageView für diese Lösung zu sein. Die andere Lösung funktioniert nur mit einem UIImage.
Adriendenat
1
Ich habe diesen Ansatz ausprobiert, aber dies scheint in iOS 8
bdv
2
Für Swift-Code sollte CGImage in die Datei mask.contents eingefügt werden. Nicht [CGImage]. Wenn Sie die Klammer [] verwenden, bedeutet dies Array.
Strawnut
59

Das Tutorial verwendet diese Methode mit zwei Parametern: imageund maskImagediese müssen Sie beim Aufrufen der Methode festlegen. Ein Beispielaufruf könnte folgendermaßen aussehen, vorausgesetzt, die Methode befindet sich in derselben Klasse und die Bilder befinden sich in Ihrem Bundle:

Hinweis - erstaunlicherweise müssen die Bilder nicht einmal die gleiche Größe haben.

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

Nachdem Sie Ihren Code angegeben haben, habe ich einige Nummern als Kommentare als Referenz hinzugefügt. Sie haben noch zwei Möglichkeiten. Diese ganze Sache ist eine Methode, die Sie irgendwo aufrufen. Sie müssen die darin enthaltenen Bilder nicht erstellen. Dadurch wird die Wiederverwendbarkeit der Methode auf Null reduziert.

Damit Ihr Code funktioniert. Ändern Sie den Methodenkopf ( 1. ) in

- (UIImage *)maskImageMyImages {

Ändern Sie dann den Namen der Variablen in 2. in

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

Die Methode gibt Ihre maskierten Bilder zurück, sodass Sie diese Methode an einer bestimmten Stelle aufrufen müssen. Können Sie uns den Code zeigen, in dem Sie Ihre Methode aufrufen?

Nick Weaver
quelle
Es tut uns leid ! Wo soll man UIImage *imageCode schreiben ? !!! denn auf dem -(UIimage *) maskeImageCompiler gibt es einen Neudefinitionsfehler!
Mc.Lover
Entschuldigung, würden Sie bitte einen Beispielcode für mich hochladen?
Ich bin
1
Nicht möglich, alles ist da. Sie müssen uns zeigen, wie Sie diese Methode aufrufen, oder zumindest den Teil, in dem Sie Ihre Bilder maskieren und das Ergebnis anzeigen möchten.
Nick Weaver
Mein Problem ist es, es zu nennen, sollte es so etwas sein? (viewDidLoad): siehe meinen bearbeiteten Code
Mc.Lover
Jede Idee zur Umkehrmaskierung (Umkehrung des obigen Prozesses). Bedeutet, dass ich den Füllbereich (transparent machen) und den transparenten Bereich mit Füllung entfernen möchte.
Mailand Patel
16

Swift 3 - Einfachste Lösung, die ich gefunden habe

Ich habe eine @IBDesignabledafür erstellt, damit Sie den Effekt sofort auf Ihrem Storyboard sehen können.

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

Wie benutzt man

  1. Erstellen Sie eine neue Datei und fügen Sie den obigen Code ein.
  2. Fügen Sie UIImageView zu Ihrem Storyboard hinzu (weisen Sie ein Bild zu, wenn Sie möchten).
  3. Im Identitätsinspektor: Ändern Sie die benutzerdefinierte Klasse in "UIImageViewWithMask" (Name der benutzerdefinierten Klasse oben).
  4. Im Attributinspektor: Wählen Sie das Maskenbild aus, das Sie verwenden möchten.

Beispiel

Maskierung auf Storyboard

Anmerkungen

  • Ihr Maskenbild sollte einen transparenten Hintergrund (PNG) für den nicht schwarzen Teil haben.
Mark Moeykens
quelle
12

Ich habe beide Codes entweder mit CALayer oder CGImageCreateWithMask ausprobiert, aber keiner von ihnen hat bei mir nicht funktioniert

Aber ich fand heraus, dass das Problem mit dem PNG-Dateiformat ist, NICHT mit dem Code !!
Also nur um meine Erkenntnisse zu teilen!

Wenn Sie verwenden möchten

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

Sie müssen verwenden 24bit png ohne Alpha - Kanal

Wenn Sie eine CALayer-Maske verwenden möchten, müssen Sie (24-Bit- oder 8-Bit-) PNG mit Alpha-Kanal verwenden, wobei ein transparenter Teil Ihres PNG das Bild maskiert (für eine Alpha-Maske mit glattem Farbverlauf verwenden Sie 24-Bit-PNG mit Alpha-Kanal).

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

Das funktioniert bei mir.

ZYiOS
quelle
Vielen Dank, diese Antwort ist besser als die anderer, die über Leistung mit mehr und größeren Bildern sprechen.
Radu Ursache
1
Jede Idee zur umgekehrten Maskierung (Umkehrung des obigen Vorgangs). Bedeutet, dass ich den Füllbereich (transparent machen) und den transparenten Bereich mit Füllung entfernen möchte.
Mailand Patel
Danke, dieser Code funktioniert besser als zuerst !! Aber ich habe zwei Probleme: Warnung bei "kCGImageAlphaPremultipliedLast" (ich habe "CGBitmapInfo" zur Behebung verwendet) UND funktioniert nicht für Retina-Bilder !!
Kannst
@Milanpatel Die einfache Lösung besteht darin, Ihre Farben im Maskenbild einfach zu invertieren. :)
Dids
@ZyiOS Wie kann ich den Rahmen des maskierten Bildes anpassen? Das heißt, der Benutzer kann die Größe des maskierten Bildes erhöhen oder verringern.
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

}

// für den Einsatz

testImage.image = maskImage(image: UIImage(named: "OriginalImage")!, withMask: UIImage(named: "maskImage")!)
Ahmed Safadi
quelle
Können Sie mir das Bild geben
Ahmed Safadi
3

Schnelle Version der akzeptierten Lösung:

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

}

Nur für den Fall, dass jemand es braucht.

Es funktioniert einwandfrei für mich.

Juan Valera
quelle
Haben Sie eine Swift 3.0-Version davon?
Van Du Tran
2

Wir haben festgestellt, dass die richtige Antwort jetzt nicht funktioniert, und haben eine kleine Drop-In-Klasse implementiert, mit deren Hilfe jedes Bild in UIImageView maskiert werden kann.

Es ist hier verfügbar: https://github.com/Werbary/WBMaskedImageView

Beispiel:

WBMaskedImageView *imgView = [[WBMaskedImageView alloc] initWithFrame:frame];
imgView.originalImage = [UIImage imageNamed:@"original_image.png"];
imgView.maskImage = [UIImage imageNamed:@"mask_image.png"];
werbary
quelle
1
Gut! Es hat wirklich geholfen.
Abhimuralidharan