Как я могу замаскировать UIImageView?


100

Я пытаюсь замаскировать изображение чем-то вроде этого:

изображение, которое нужно замаскировать

Не могли бы вы мне помочь?

Я использую этот код:

- (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
Я человек, который написал оригинальный учебник. Изображение маски - это простое изображение в оттенках серого, которое я создал в фотошопе. Ничего особенного в этом нет. Черная область становится «прозрачной» частью маски. Имейте в виду, что любой оттенок серого интерпретируется как степень непрозрачности. Таким образом, маски также могут быть градиентами, что полезно для создания более мягких границ вокруг маски.
ra9r

Он должен быть такого же размера, правда?
KarenAnne

гипер-элегантный код, спасибо. просто полезная ссылка, если кому-то нужно обрезать изображение перед маскированием ... stackoverflow.com/questions/17712797/…
Fattie 04

Есть идеи по этому поводу .. stackoverflow.com/questions/28122370/…
Милан Патель,

1
Я настоятельно прошу вас изменить выбранный ответ на тот, за который почти в 3 раза больше голосов. На мой взгляд, это лучше во всех смыслах.
Альберт Реншоу,

Ответы:


165

Есть способ попроще.

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

Для 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но ничего не произошло!
Mc.Lover

6
Вы должны установить свойство слоя для использования маски, то есть [yourImageView.layer setMasksToBounds: YES];
Wolfgang Schreurs

3
Обратите внимание, что для этого решения необходимо использовать UIImageView. Другое решение работает только с UIImage.
adriendenat 05

1
Я пробовал этот подход, но похоже, что он не работает в iOS 8
bdv

2
Для кода Swift, когда он помещен в mask.contents, должен CGImage. Не [CGImage]. Если использовать скобку [], это означает массив.
Strawnut

59

В учебнике используется этот метод с двумя параметрами: imageи maskImage, которые вы должны установить при вызове метода. Пример вызова может выглядеть так, если метод находится в том же классе, а изображения находятся в вашем пакете:

Обратите внимание: изображения даже не обязательно должны быть одинакового размера.

...
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компилятор выдает ошибку переопределения!
Mc.Lover

извините, не могли бы вы загрузить для меня образец кода? Я смущен: -s
Mc.Lover

1
Невозможно, все есть. Вам нужно будет показать нам, как вы вызываете этот метод, или, по крайней мере, ту часть, в которой вы хотите замаскировать свои изображения и показать результат.
Ник Уивер,

моя проблема в том, чтобы назвать это, это должно быть что-то вроде этого ?? (viewDidLoad): см. мой отредактированный код
Mc.Lover

Любое представление об обратном маскировании. (Обратный процесс выше). Означает, что я хочу удалить область заливки (сделать ее прозрачной) и прозрачную область с заливкой.
Милан Патель

17

Swift 3 - простейшее решение, которое я нашел

Я создал @IBDesignableдля этого, чтобы вы могли сразу увидеть эффект на своей раскадровке.

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. Добавьте UIImageView в свою раскадровку (назначьте изображение, если хотите).
  3. В Identity Inspector: измените настраиваемый класс на «UIImageViewWithMask» (имя настраиваемого класса выше).
  4. В инспекторе атрибутов: выберите изображение маски, которое вы хотите использовать.

пример

Маскирование на раскадровке

Ноты

  • Изображение маски должно иметь прозрачный фон (PNG) для не черной части.

12

Я пробовал оба кода, используя CALayer или CGImageCreateWithMask, но ни один из них не работал у меня

Но я обнаружил, что проблема в формате файла png, а НЕ в коде !!
так что просто хочу поделиться своим открытием!

Если вы хотите использовать

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

вы должны использовать 24-битный png без альфа-канала

Если вы хотите использовать маску CALayer, вы должны использовать (24-битный или 8-битный) png с альфа-каналом, где прозрачная часть вашего png будет маскировать изображение (для альфа-маски с плавным градиентом .. используйте 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
Есть идеи по поводу обратной маскировки. (Обратный процесс выше). Значит, я хочу удалить область заливки (сделать ее прозрачной) и прозрачную область с заливкой.
Милан Патель

Спасибо, этот код работает лучше, чем первый !! Но у меня две проблемы: Предупреждение о «kCGImageAlphaPremultipliedLast» (я использовал «CGBitmapInfo», чтобы исправить это) И не работает для изображений сетчатки !! Ты можешь мне помочь ?? ...
fingerup

@Milanpatel Простое решение - просто инвертировать цвета в изображении маски. :)
Dids

@ZyiOS, как я могу настроить рамку замаскированного изображения. т.е. пользователь может увеличивать или уменьшать размер замаскированного изображения.
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

}

// для использования

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


3

Быстрая версия принятого решения:

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.