Как я могу замаскировать 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.