Зміна кольору UIImage


100

Я намагаюся змінити колір UIImage. Мій код:

-(UIImage *)coloredImage:(UIImage *)firstImage withColor:(UIColor *)color {
    UIGraphicsBeginImageContext(firstImage.size);

    CGContextRef context = UIGraphicsGetCurrentContext();
    [color setFill];

    CGContextTranslateCTM(context, 0, firstImage.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    CGContextSetBlendMode(context, kCGBlendModeCopy);
    CGRect rect = CGRectMake(0, 0, firstImage.size.width, firstImage.size.height);
    CGContextDrawImage(context, rect, firstImage.CGImage);

    CGContextClipToMask(context, rect, firstImage.CGImage);
    CGContextAddRect(context, rect);
    CGContextDrawPath(context,kCGPathElementMoveToPoint);

    UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return coloredImg;
}

Цей код працює, але отримане зображення не так добре, як повинно бути: межі пікселів поверненого зображення є переривчастими і не такими гладкими, як на моєму першому зображенні. Як я можу вирішити цю проблему?


1
можливий дублікат зміни кольору UIImage?
idmean

Відповіді:


263

З iOS 7 це найпростіший спосіб зробити це.

Завдання-C:

theImageView.image = [theImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[theImageView setTintColor:[UIColor redColor]];

Swift 2.0:

theImageView.image = theImageView.image?.imageWithRenderingMode(.AlwaysTemplate) 
theImageView.tintColor = UIColor.magentaColor()

Свіфт 4.0:

theImageView.image = theImageView.image?.withRenderingMode(.alwaysTemplate) 
theImageView.tintColor = .magenta

Дошка розкадрування:

Спочатку налаштуйте зображення як шаблон (на правій панелі - Візуалізувати як) у своїх ресурсах. Тоді кольором зображення буде застосований колір відтінку. введіть тут опис зображення


"UIImageRenderingModeAlwaysTemplate: Завжди малюйте зображення як шаблонне зображення, ігноруючи інформацію про колір." Приємно!
Тіме

3
У Swift 2.0+theImageView.image? = (theImageView.image?.imageWithRenderingMode(.AlwaysTemplate))! theImageView.tintColor = UIColor.magentaColor()
ColossalChris

@AnkishJain чи є якісь проблеми щодо продуктивності навколо цього підходу?
Ríomhaire

3
Це не змінює колір зображення, а навпаки, наказує виду подати його з іншим відтінком (кольором).
Стів Куо,

@Womble не дуже. Ви можете використовувати це для будь-якого UIImage насправді. img = [img imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [button setTintColor:[UIColor redColor]]; [button setImage:img forState:UIControlStateNormal];@Ankish дякую!
Мотасим

31

Це майже відповідь вище, але трохи скорочена. Це сприймає зображення лише як маску, а насправді не «примножує» і не забарвлює зображення.

Завдання C:

    UIColor *color = <# UIColor #>;
    UIImage *image = <# UIImage #>;// Image to mask with
    UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [color setFill];
    CGContextTranslateCTM(context, 0, image.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextClipToMask(context, CGRectMake(0, 0, image.size.width, image.size.height), [image CGImage]);
    CGContextFillRect(context, CGRectMake(0, 0, image.size.width, image.size.height));

    UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

Швидкий:

    let color: UIColor = <# UIColor #>
    let image: UIImage = <# UIImage #> // Image to mask with
    UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)
    let context = UIGraphicsGetCurrentContext()
    color.setFill()
    context?.translateBy(x: 0, y: image.size.height)
    context?.scaleBy(x: 1.0, y: -1.0)
    context?.clip(to: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height), mask: image.cgImage!)
    context?.fill(CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
    let coloredImg = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

2
Ви повинні використовувати, UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);оскільки ваша версія створюватиме лише графіку без сітківки.
Nikola Lajic

user1270061 - це спосіб зробити це, на мій досвід. Інша відповідь "спалення" потребує, очевидно, вихідного зображення певного кольору. Цей просто використовує значення альфа у вихідних пікселях та поєднує їх із бажаним кольором - ідеально.
Джейсон

Ідеально - єдина відповідь, яка для мене добре працювала. (16 травня ')
trdavidson

10

Інший спосіб підфарбовувати зображення - просто помножити його на постійний колір. Іноді це переважно, оскільки воно не "піднімає" значення кольорів у чорних областях; він зберігає відносну інтенсивність зображення однаковою. Використання накладки як відтінок, як правило, згладжує контраст.

Це код, який я використовую:

UIImage *MultiplyImageByConstantColor( UIImage *image, UIColor *color ) {

    CGSize backgroundSize = image.size;
    UIGraphicsBeginImageContext(backgroundSize);

    CGContextRef ctx = UIGraphicsGetCurrentContext();

    CGRect backgroundRect;
    backgroundRect.size = backgroundSize;
    backgroundRect.origin.x = 0;
    backgroundRect.origin.y = 0;

    CGFloat r,g,b,a;
    [color getRed:&r green:&g blue:&b alpha:&a];
    CGContextSetRGBFillColor(ctx, r, g, b, a);
    CGContextFillRect(ctx, backgroundRect);

    CGRect imageRect;
    imageRect.size = image.size;
    imageRect.origin.x = (backgroundSize.width - image.size.width)/2;
    imageRect.origin.y = (backgroundSize.height - image.size.height)/2;

    // Unflip the image
    CGContextTranslateCTM(ctx, 0, backgroundSize.height);
    CGContextScaleCTM(ctx, 1.0, -1.0);

    CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
    CGContextDrawImage(ctx, imageRect, image.CGImage);

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return newImage;
}

Швидка версія

extension UIImage{

    static func multiplyImageByConstantColor(image:UIImage,color:UIColor)->UIImage{
        let backgroundSize = image.size
        UIGraphicsBeginImageContext(backgroundSize)

        let ctx = UIGraphicsGetCurrentContext()

        var backgroundRect=CGRect()
        backgroundRect.size = backgroundSize
        backgroundRect.origin.x = 0
        backgroundRect.origin.y = 0

        var r:CGFloat
        var g:CGFloat
        var b:CGFloat
        var a:CGFloat
        color.getRed(&r, green: &g, blue: &b, alpha: &a)
        CGContextSetRGBFillColor(ctx, r, g, b, a)
        CGContextFillRect(ctx, backgroundRect)

        var imageRect=CGRect()
        imageRect.size = image.size
        imageRect.origin.x = (backgroundSize.width - image.size.width)/2
        imageRect.origin.y = (backgroundSize.height - image.size.height)/2

        // Unflip the image
        CGContextTranslateCTM(ctx, 0, backgroundSize.height)
        CGContextScaleCTM(ctx, 1.0, -1.0)

        CGContextSetBlendMode(ctx, .Multiply)
        CGContextDrawImage(ctx, imageRect, image.CGImage)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage
    }
}

Саме рішення, яке я шукав! Дякую!
vir us

Що робити, якщо я хочу зробити віртуальний додаток для розпису стін? це працює, а що, якщо хочеться просто розфарбувати стіни на зображенні. Погляньте на це посилання, будь ласка: - stackoverflow.com/questions/27482508/…
Шайлеш

Це працює набагато краще, ніж деякі інші рішення.
Метт Хадсон,

3
Це тонувало лише фон об’єкта для мене, а не сам об’єкт.
user3562927

7

У Swift 3.0

imageView.image? = (imageView.image?.withRenderingMode(.alwaysTemplate))!
imageView.tintColor = UIColor.magenta

У Swift 2.0

yourImage.image? = (yourImage.image?.imageWithRenderingMode(.AlwaysTemplate))!
yourImage.tintColor = UIColor.magentaColor()

Насолоджуйтесь швидкими піонерами


5
З кодом все гаразд, але пост стосується UIImage. Ми не завжди маємо справу з UIImageViews.
HenryRoot, два

5

Свіфт 4.2 Рішення

extension UIImage {
    func withColor(_ color: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        guard let ctx = UIGraphicsGetCurrentContext(), let cgImage = cgImage else { return self }
        color.setFill()
        ctx.translateBy(x: 0, y: size.height)
        ctx.scaleBy(x: 1.0, y: -1.0)
        ctx.clip(to: CGRect(x: 0, y: 0, width: size.width, height: size.height), mask: cgImage)
        ctx.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
        guard let colored = UIGraphicsGetImageFromCurrentImageContext() else { return self }
        UIGraphicsEndImageContext()
        return colored
    }
}

// Usage:
// let redImage = UIImage().withColor(.red)

5

Починаючи з iOS 10, ви можете використовувати UIGraphicsImageRenderer:

extension UIImage {

    func colored(_ color: UIColor) -> UIImage {
        let renderer = UIGraphicsImageRenderer(size: size)
        return renderer.image { context in
            color.setFill()
            self.draw(at: .zero)
            context.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height), blendMode: .sourceAtop)
        }
    }

}

3

Якщо вам не потрібно робити це програмно, ви можете просто зробити це за допомогою інтерфейсу Xcode.

Якщо ви перейдете до зображення в папці ваших ресурсів зображення, відкрийте інспектор праворуч, і з’явиться розкривне меню "Візуалізувати як" із наступними параметрами:

  1. За замовчуванням
  2. Оригінальний
  3. Шаблон

Зробивши вибір шаблону, ви можете змінити tintColor зображення як завгодно - незалежно від того, використовує він інтерфейс розкадровки Xcode або програмно.

введіть тут опис зображення

Подивіться це зображення:

введіть тут опис зображення


Ви використовуєте XCODE 8?
doxsi

2

Ось моя адаптація відповіді @ Anna. Тут є два ключові моменти:

  • Використовуйте destinationInрежим змішування
  • Телефонуйте, UIGraphicsBeginImageContextWithOptions(backgroundSize, false, UIScreen.main.scale)щоб отримати плавне зображення

Код у Swift 3 :

extension UIImage {

    static func coloredImage(image: UIImage?, color: UIColor) -> UIImage? {

        guard let image = image else {
            return nil
        }

        let backgroundSize = image.size
        UIGraphicsBeginImageContextWithOptions(backgroundSize, false, UIScreen.main.scale)

        let ctx = UIGraphicsGetCurrentContext()!

        var backgroundRect=CGRect()
        backgroundRect.size = backgroundSize
        backgroundRect.origin.x = 0
        backgroundRect.origin.y = 0

        var r:CGFloat = 0
        var g:CGFloat = 0
        var b:CGFloat = 0
        var a:CGFloat = 0
        color.getRed(&r, green: &g, blue: &b, alpha: &a)
        ctx.setFillColor(red: r, green: g, blue: b, alpha: a)
        ctx.fill(backgroundRect)

        var imageRect = CGRect()
        imageRect.size = image.size
        imageRect.origin.x = (backgroundSize.width - image.size.width) / 2
        imageRect.origin.y = (backgroundSize.height - image.size.height) / 2

        // Unflip the image
        ctx.translateBy(x: 0, y: backgroundSize.height)
        ctx.scaleBy(x: 1.0, y: -1.0)

        ctx.setBlendMode(.destinationIn)
        ctx.draw(image.cgImage!, in: imageRect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }
}

2
чому ви передаєте зображення при розширенні UIImage? вам слід видалити статичне ключове слово вашого методу і просто використовувати selfвсередині вашого методу і видалити непотрібний параметр зображення
Лео Дабус

1

Спираючись на відповідь @ Anna, я переписую на швидку 2.2 і обробляю зображення з альфа-каналом:

static func multiplyImageByConstantColor(image:UIImage,color:UIColor)->UIImage{
    let backgroundSize = image.size
    UIGraphicsBeginImageContext(backgroundSize)

    let ctx = UIGraphicsGetCurrentContext()

    var backgroundRect=CGRect()
    backgroundRect.size = backgroundSize
    backgroundRect.origin.x = 0
    backgroundRect.origin.y = 0

    var r:CGFloat = 0
    var g:CGFloat = 0
    var b:CGFloat = 0
    var a:CGFloat = 0
    color.getRed(&r, green: &g, blue: &b, alpha: &a)
    CGContextSetRGBFillColor(ctx, r, g, b, a)

    // Unflip the image
    CGContextTranslateCTM(ctx, 0, backgroundSize.height)
    CGContextScaleCTM(ctx, 1.0, -1.0)
    CGContextClipToMask(ctx, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage);
    CGContextFillRect(ctx, backgroundRect)

    var imageRect=CGRect()
    imageRect.size = image.size
    imageRect.origin.x = (backgroundSize.width - image.size.width)/2
    imageRect.origin.y = (backgroundSize.height - image.size.height)/2


    CGContextSetBlendMode(ctx, .Multiply)
    CGContextDrawImage(ctx, imageRect, image.CGImage)

    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage
}

0

Код Анни добре працює для копіювання UIImage.image на кольоровий фон .image за допомогою kCGBlendModeNormal, а не kCGBlendModeMultiply. Наприклад, self.mainImage.image = [self NormalImageByConstantColor: self.mainImage.image withColor: yourColor];розмістить вміст mainImage.image над відтінком yourColor, зберігаючи непрозорість yourColor. Це вирішило мою проблему з розміщенням фонового кольору з прозорістю за зображенням, яке буде збережено в рулоні камери.


0

Свіфт 3:

extension UIImage{

    static func multiplyImageByConstantColor(image:UIImage,color:UIColor) -> UIImage{

        let backgroundSize = image.size
        UIGraphicsBeginImageContext(backgroundSize)

        guard let ctx = UIGraphicsGetCurrentContext() else {return image}

        var backgroundRect=CGRect()
        backgroundRect.size = backgroundSize
        backgroundRect.origin.x = 0
        backgroundRect.origin.y = 0

        var r:CGFloat = 0
        var g:CGFloat = 0
        var b:CGFloat = 0
        var a:CGFloat = 0
        color.getRed(&r, green: &g, blue: &b, alpha: &a)
        ctx.setFillColor(red: r, green: g, blue: b, alpha: a)

        // Unflip the image
        ctx.translateBy(x: 0, y: backgroundSize.height)
        ctx.scaleBy(x: 1.0, y: -1.0)
        ctx.clip(to: CGRect(0, 0, image.size.width, image.size.height), mask: image.cgImage!)
        ctx.fill(backgroundRect)


        var imageRect=CGRect()
        imageRect.size = image.size
        imageRect.origin.x = (backgroundSize.width - image.size.width)/2
        imageRect.origin.y = (backgroundSize.height - image.size.height)/2


        ctx.setBlendMode(.multiply)
        ctx.draw(image.cgImage!, in: imageRect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }
}

0

Swift 3.0 версія чудового коду Анни:

extension UIImage{

    static func multiplyImageByConstantColor(image:UIImage,color:UIColor)-> UIImage {
        let backgroundSize = image.size
        UIGraphicsBeginImageContext(backgroundSize)

        let ctx = UIGraphicsGetCurrentContext()!

        var backgroundRect=CGRect()
        backgroundRect.size = backgroundSize
        backgroundRect.origin.x = 0
        backgroundRect.origin.y = 0

        let myFloatForR = 0
        var r = CGFloat(myFloatForR)
        let myFloatForG = 0
        var g = CGFloat(myFloatForG)
        let myFloatForB = 0
        var b = CGFloat(myFloatForB)
        let myFloatForA = 0
        var a = CGFloat(myFloatForA)

        color.getRed(&r, green: &g, blue: &b, alpha: &a)
        ctx.setFillColor(red: r, green: g, blue: b, alpha: a)
        ctx.fill(backgroundRect)

        var imageRect=CGRect()
        imageRect.size = image.size
        imageRect.origin.x = (backgroundSize.width - image.size.width)/2
        imageRect.origin.y = (backgroundSize.height - image.size.height)/2

        // Unflip the image
        ctx.translateBy(x: 0, y: backgroundSize.height)
        ctx.scaleBy(x: 1.0, y: -1.0)

        ctx.setBlendMode(.multiply)
        ctx.draw(image.cgImage!, in: imageRect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }
}

0

Для iOS 13 та новіших версій:

let redImage = image.withTintColor(.red, renderingMode: .alwaysTemplate)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.