UIImage rengini değiştirme


101

UIImage'ın rengini değiştirmeye çalışıyorum. Kodum:

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

Bu kod işe yarıyor, ancak elde edilen görüntü o kadar iyi değil: döndürülen görüntünün sınır pikselleri aralıklı ve ilk görüntümdeki kadar pürüzsüz değil. Bu sorunu nasıl çözebilirim?


Yanıtlar:


267

İOS 7'den beri, bunu yapmanın en basit yolu budur.

Amaç-C:

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

Swift 2.0:

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

Swift 4.0:

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

Storyboard:

Önce görüntüyü varlıklarınızda şablon olarak (sağ çubukta - Farklı Oluştur) yapılandırın. Ardından görüntünün rengi, uygulanan ton rengi olur. görüntü açıklamasını buraya girin


"UIImageRenderingModeAlwaysTemplate: Görüntüyü her zaman renk bilgilerini göz ardı ederek bir şablon görüntüsü olarak çizin." Güzel!
Tieme

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

@AnkishJain bu yaklaşımla ilgili herhangi bir performans endişesi var mı?
Ríomhaire

3
Bu, görüntünün rengini değiştirmez, bunun yerine görünüme onu farklı bir tonla (renkle) oluşturma talimatını verir.
Steve Kuo

@Womble gerçekten değil. Bunu gerçekten herhangi bir UIImage için kullanabilirsiniz. img = [img imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [button setTintColor:[UIColor redColor]]; [button setImage:img forState:UIControlStateNormal];@Ankish teşekkürler!
Motasim

31

Bu hemen hemen yukarıdaki cevaptır, ancak biraz kısaltılmıştır. Bu yalnızca görüntüyü bir maske olarak alır ve görüntüyü gerçekte "çoğaltmaz" veya renklendirmez.

Hedef 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();

Swift:

    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
Kullanmalısınız UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);çünkü sürümünüz sadece retina olmayan grafikler oluşturacaktır.
Nikola Lajic

Benim deneyimime göre user1270061 bunu yapmanın yoludur. "Yakma" ile ilgili diğer cevap, belli bir rengin kaynak görüntüsüne ihtiyaç duyar. Bu, kaynak piksellerdeki alfa değerlerini kullanır ve bunu istenen renkle birleştirir - mükemmel.
Jason

Mükemmel - sadece benim için işe yarayan yanıt. (16 Mayıs)
trdavidson

10

Bir görüntüyü renklendirmenin başka bir yolu da onu sabit bir renkle çarpmaktır. Bazen bu tercih edilebilir çünkü siyah alanlardaki renk değerlerini "yükseltmez"; görüntüdeki göreceli yoğunlukları aynı tutar. Renk tonu olarak bir kaplama kullanmak kontrastı düzleştirme eğilimindedir.

Kullandığım kod bu:

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

Swift versiyonu

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

Tam aradığım çözüm! Teşekkürler!
vir bize

Ya sanal bir duvar resmi uygulaması yapmak istersem? bu işe yarar, ya sadece resimdeki duvarları renklendirmek istersen. Lütfen bu bağlantıya bir göz atın: - stackoverflow.com/questions/27482508/…
Shailesh

Bu, diğer bazı çözümlerden çok daha iyi çalışıyor.
Matt Hudson

3
Bu benim için yalnızca nesnenin arka planını renklendirdi, nesnenin kendisini değil.
user3562927

7

Swift 3.0'da

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

Swift 2.0'da

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

Keyfinize bakın Swift öncüleri


5
Kod tamam, ancak gönderi UIImage hakkındadır. Her zaman UIImageViews ile uğraşmıyoruz.
HenryRootTwo

7

İOS 10'dan başlayarak şunları kullanabilirsiniz 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)
        }
    }

}

6

Swift 4.2 Çözümü

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)

3

Programlı olarak yapmanız gerekmiyorsa, bunu Xcode UI kullanarak yapabilirsiniz.

Görüntü varlıkları klasörünüzdeki resme giderseniz, sağ taraftaki denetçiyi açın ve aşağıdaki seçenekleri içeren bir "Farklı Oluştur" açılır menüsü vardır:

  1. Varsayılan
  2. Orijinal
  3. Şablon

Şablon seçimini yaptıktan sonra, Xcode film şeridi kullanıcı arayüzünü veya programlı olarak görüntünün ton rengini istediğiniz gibi değiştirebilirsiniz.

görüntü açıklamasını buraya girin

Bu resme bakın:

görüntü açıklamasını buraya girin


XCODE 8 kullanıyor musunuz?
doxsi

2

İşte @ Anna'nın cevabına uyarlamam. Burada iki önemli nokta:

  • destinationInKarışım modunu kullan
  • UIGraphicsBeginImageContextWithOptions(backgroundSize, false, UIScreen.main.scale)Pürüzsüz görüntü elde etmek için arayın

İn Kod 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'ı genişletirken neden görüntüyü iletiyorsunuz? yönteminizin statik anahtar kelimesini kaldırmanız ve sadece yönteminizin selfiçinde kullanmanız ve gereksiz görüntü parametresini kaldırmanız gerekir
Leo Dabus

1

@ Anna'nın cevabına dayanıyor ve hızlı 2.2 için yeniden yazıyorum ve alfa kanalıyla görüntüyü işliyor:

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

Anna'nın kodu, kCGBlendModeMultiply yerine kCGBlendModeNormal kullanarak renkli bir .image arka planı üzerinde bir UIImage.image kopyalamak için iyi çalışıyor. Örneğin self.mainImage.image = [self NormalImageByConstantColor: self.mainImage.image withColor: yourColor];, yourColor'unuzun opaklığını korurken mainImage.image içeriğini yourColor tonunun üzerine yerleştirir. Bu, Kamera Rulosuna kaydedilecek bir görüntünün arkasına opaklığa sahip bir arka plan rengi yerleştirme sorunumu çözdü.


0

Swift 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

Anna'nın harika kodunun Swift 3.0 sürümü:

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

İOS 13 ve daha yeni sürümler için:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.