Zmień kolor png w przyciskach - iOS

95

Mam zestaw ikon, które utworzyłem, które są przezroczystymi białymi plikami PNG:

wprowadź opis obrazu tutaj

Chciałbym móc zabarwić je na inne kolory. Takich jak niebieski, szary itp.

Zauważyłem, że „kliknięte / dotknięte” zmieniają się automatycznie na szare. Zakładam więc, że mogę zmienić ten szary na cokolwiek mi się podoba, za pomocą kranu lub jego normalnego stanu:

wprowadź opis obrazu tutaj

Jaki byłby najlepszy sposób, aby to osiągnąć?

Galaktyka
źródło

Odpowiedzi:

231

Poniższy kod ustawi kolor odcień dla normalnego stanu przycisku:

Dla Swift 4 i nowszych:

let origImage = UIImage(named: "imageName")
let tintedImage = origImage?.withRenderingMode(.alwaysTemplate)
btn.setImage(tintedImage, for: .normal)
btn.tintColor = .red

Możesz zmienić odcień koloru w zależności od potrzeb, gdy zmieni się stan przycisku.


starsza wersja

Dla Swift 3:

let origImage = UIImage(named: "imageName")
let tintedImage = origImage?.withRenderingMode(.alwaysTemplate)
btn.setImage(tintedImage, forState: .normal)
btn.tintColor = .redColor

W przypadku Swift 2: zobacz historię wersji.

Yuvrajsinh
źródło
Wspaniale! Ale jak przywrócić oryginalny obraz (nie przyciemniony)?
Marsman,
@Marsman, jeśli spojrzysz na moje rozwiązanie, originaImage reprezentuje oryginalny obraz, a tintedImage reprezentuje przyciemnioną wersję oryginalnego obrazu, więc nie zastępujesz oryginalnego obrazu, ale tworzysz jego nową wersję i będziesz mieć dwie wersje tego samego obrazu, których możesz użyć jako na twoje potrzeby
Yuvrajsinh
nie działa w iOS 11 z xcode 9.1. Po dodaniu koloru tinta całkowicie zmienia kolor imageView.
Vineesh TP
Różne zmiany wprowadziły nieścisłości w tej odpowiedzi.
Eric Aya
13

W systemie iOS 7 wprowadzono właściwość o nazwie tintColor dla widoków (w tym UIImageView). Jednak musisz również ustawić typ renderowania w UIImage, aby miało to jakikolwiek efekt.

UIImage *originalImage = [UIImage imageNamed:@"image.png"];
UIImage *tintedImage = [originalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIImageView *imageView = [[UIImageView alloc] initWithImage:tintedImage];

imageView.tintColor = [UIColor grayColor];
[self.view addSubview:imageView];

Powinno to przynieść efekt, do którego dążysz w stanie domyślnym.

JoGoFo
źródło
10

Możesz użyć tego rozszerzenia:

import UIKit

extension CGContext {

    func fill(_ rect: CGRect,
              with mask: CGImage,
              using color: CGColor) {

        saveGState()
        defer { restoreGState() }

        translateBy(x: 0.0, y: rect.size.height)
        scaleBy(x: 1.0, y: -1.0)
        setBlendMode(.normal)

        clip(to: rect, mask: mask)

        setFillColor(color)
        fill(rect)
    }
}

extension UIImage {

    func filled(with color: UIColor) -> UIImage {
        let rect = CGRect(origin: .zero, size: self.size)
        guard let mask = self.cgImage else { return self }

        if #available(iOS 10.0, *) {
            let rendererFormat = UIGraphicsImageRendererFormat()
            rendererFormat.scale = self.scale

            let renderer = UIGraphicsImageRenderer(size: rect.size,
                                                   format: rendererFormat)
            return renderer.image { context in
                context.cgContext.fill(rect,
                                       with: mask,
                                       using: color.cgColor)
            }
        } else {
            UIGraphicsBeginImageContextWithOptions(rect.size,
                                                   false,
                                                   self.scale)
            defer { UIGraphicsEndImageContext() }

            guard let context = UIGraphicsGetCurrentContext() else { return self }

            context.fill(rect,
                         with: mask,
                         using: color.cgColor)
            return UIGraphicsGetImageFromCurrentImageContext() ?? self
        }
    }
}
Ilia Khalyapin
źródło
2
U mnie to też nie działa. Kiedy używam tej metody, widzę tylko kolor, a nie obraz zabarwiony na ten kolor.
Moebius,
9

Aby zmienić odcień obrazu ( wybierz , klasyczny obraz , zdjęcie ) użyj tego:

Przykładowe zdjęcie: wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

Szybki 2

public extension UIImage {
    /**
     Tint, Colorize image with given tint color<br><br>
     This is similar to Photoshop's "Color" layer blend mode<br><br>
     This is perfect for non-greyscale source images, and images that have both highlights and shadows that should be preserved<br><br>
     white will stay white and black will stay black as the lightness of the image is preserved<br><br>

     <img src="http://yannickstephan.com/easyhelper/tint1.png" height="70" width="120"/>

     **To**

     <img src="http://yannickstephan.com/easyhelper/tint2.png" height="70" width="120"/>

     - parameter tintColor: UIColor

     - returns: UIImage
     */
    public func tintPhoto(tintColor: UIColor) -> UIImage {

        return modifiedImage { context, rect in
            // draw black background - workaround to preserve color of partially transparent pixels
            CGContextSetBlendMode(context, .Normal)
            UIColor.blackColor().setFill()
            CGContextFillRect(context, rect)

            // draw original image
            CGContextSetBlendMode(context, .Normal)
            CGContextDrawImage(context, rect, self.CGImage)

            // tint image (loosing alpha) - the luminosity of the original image is preserved
            CGContextSetBlendMode(context, .Color)
            tintColor.setFill()
            CGContextFillRect(context, rect)

            // mask by alpha values of original image
            CGContextSetBlendMode(context, .DestinationIn)
            CGContextDrawImage(context, rect, self.CGImage)
        }
    }
    /**
     Tint Picto to color

     - parameter fillColor: UIColor

     - returns: UIImage
     */
    public func tintPicto(fillColor: UIColor) -> UIImage {

        return modifiedImage { context, rect in
            // draw tint color
            CGContextSetBlendMode(context, .Normal)
            fillColor.setFill()
            CGContextFillRect(context, rect)

            // mask by alpha values of original image
            CGContextSetBlendMode(context, .DestinationIn)
            CGContextDrawImage(context, rect, self.CGImage)
        }
    }
    /**
     Modified Image Context, apply modification on image

     - parameter draw: (CGContext, CGRect) -> ())

     - returns: UIImage
     */
    private func modifiedImage(@noescape draw: (CGContext, CGRect) -> ()) -> UIImage {

        // using scale correctly preserves retina images
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        let context: CGContext! = UIGraphicsGetCurrentContext()
        assert(context != nil)

        // correctly rotate image
        CGContextTranslateCTM(context, 0, size.height);
        CGContextScaleCTM(context, 1.0, -1.0);

        let rect = CGRectMake(0.0, 0.0, size.width, size.height)

        draw(context, rect)

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

UPD

Szybki 3

extension UIImage {

    /**
     Tint, Colorize image with given tint color<br><br>
     This is similar to Photoshop's "Color" layer blend mode<br><br>
     This is perfect for non-greyscale source images, and images that have both highlights and shadows that should be preserved<br><br>
     white will stay white and black will stay black as the lightness of the image is preserved<br><br>

     <img src="http://yannickstephan.com/easyhelper/tint1.png" height="70" width="120"/>

     **To**

     <img src="http://yannickstephan.com/easyhelper/tint2.png" height="70" width="120"/>

     - parameter tintColor: UIColor

     - returns: UIImage
     */
    func tintPhoto(_ tintColor: UIColor) -> UIImage {

        return modifiedImage { context, rect in
            // draw black background - workaround to preserve color of partially transparent pixels
            context.setBlendMode(.normal)
            UIColor.black.setFill()
            context.fill(rect)

            // draw original image
            context.setBlendMode(.normal)
            context.draw(cgImage!, in: rect)

            // tint image (loosing alpha) - the luminosity of the original image is preserved
            context.setBlendMode(.color)
            tintColor.setFill()
            context.fill(rect)

            // mask by alpha values of original image
            context.setBlendMode(.destinationIn)
            context.draw(context.makeImage()!, in: rect)
        }
    }

    /**
     Tint Picto to color

     - parameter fillColor: UIColor

     - returns: UIImage
     */
    func tintPicto(_ fillColor: UIColor) -> UIImage {

        return modifiedImage { context, rect in
            // draw tint color
            context.setBlendMode(.normal)
            fillColor.setFill()
            context.fill(rect)

            // mask by alpha values of original image
            context.setBlendMode(.destinationIn)
            context.draw(cgImage!, in: rect)
        }
    }

    /**
     Modified Image Context, apply modification on image

     - parameter draw: (CGContext, CGRect) -> ())

     - returns: UIImage
     */
    fileprivate func modifiedImage(_ draw: (CGContext, CGRect) -> ()) -> UIImage {

        // using scale correctly preserves retina images
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        let context: CGContext! = UIGraphicsGetCurrentContext()
        assert(context != nil)

        // correctly rotate image
        context.translateBy(x: 0, y: size.height)
        context.scaleBy(x: 1.0, y: -1.0)

        let rect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)

        draw(context, rect)

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

}
YannSteph
źródło
czy możesz mi powiedzieć, jak używać tych metod, czyli jak je wywoływać?
ashmi123
To w ogóle nie działa dla mnie. Mam plik PNG ustawiony jako obraz przycisku. Kiedy używam tintPhoto lub tintPicto, po prostu nie działają.
Moebius,
czy dodasz swój obraz za pomocąRenderingMode (.alwaysTemplate)? @Moebius
YannSteph
Tak, a kiedy używam jednej z metod barwienia, widzę tylko kolor. Ustawiłem nawet alfa koloru na 0,5 i nadal widzę tylko kolor. Swift 4
Moebius
Ok zastosuj alfa po odcieniu @Moebius
YannSteph
9

Jeśli ustawiasz obraz dla przycisku, po prostu przejdź do inspektora atrybutów i zmień typ przycisku na systemowy. Następnie ustaw obraz i zmień kolor odcienia. Kolor obrazu ulegnie zmianie. Jeśli tak się nie stało, sprawdź typ przycisku.

Milligator
źródło
9

Swift 4 lub 5

extension UIButton{

    func setImageTintColor(_ color: UIColor) {
        let tintedImage = self.imageView?.image?.withRenderingMode(.alwaysTemplate)
        self.setImage(tintedImage, for: .normal)
        self.tintColor = color
    }

}

Posługiwać się:

button.setImage(UIImage(named: "image_name"), for: .normal) // You can set image direct from Storyboard
button.setImageTintColor(UIColor.white)
Krunal Patel
źródło
nie ma żadnej metody setImageTintColor dla widoku UIbutton
Wahab Khan Jadon
Jest to rozszerzenie UIButton. Stworzyłem powyżej.
Krunal Patel
6

Jeśli używasz katalogów zasobów, możesz ustawić sam zasób obrazu do renderowania w trybie szablonu. Następnie możesz ustawić tintColor przycisku w Interface Builder (lub w kodzie) i powinno to zająć.

moliveira
źródło
Jest to najlepsze podejście, jeśli używasz tego samego obrazu w wielu miejscach, ponieważ ustawisz obraz w trybie szablonu tylko raz w katalogu zasobów.
Jonathan Cabrera
To podejście nie działa dla mnie. Kiedy ustawię PNG jako obraz szablonu, a następnie ustawię kolor tinty, obraz zostanie zastąpiony kolorem tinty.
Moebius,
Dla mnie to również najlepsze podejście. Wielkie dzięki. Prosty, łatwy do wielokrotnego użytku ...
Alparslan Selçuk Develioğlu
4

Szybki 4

    let origImage = UIImage(named: "check")
    let tintedImage = origImage?.withRenderingMode(.alwaysTemplate)
    buttons[0].setImage(tintedImage, for: .normal)
    buttons[0].tintColor = .red
Ahmed Safadi
źródło
3

Jeśli używasz katalogów zasobów, możesz ustawić sam zasób obrazu do renderowania w trybie szablonu. Następnie możesz ustawić tintColor przycisku w Interface Builder (lub w kodzie) i powinno to zająć.

Ratnesh NS
źródło
3

Poniżej znalazłem najłatwiejsze podejście,

Otwórz katalog zasobów i wybierz obraz, a następnie przejdź do inspektora atrybutów i zmień Render Asna Template Imagejak poniżej

wprowadź opis obrazu tutaj

Następnie dodaj poniższy kod w metodzie działania przycisku

yourButton.tintColor = .gray
kalpa
źródło
-1

Swift 4 i 4.2

let img = UIImage.init(named: "buttonName")?.withRenderingMode(UIImageRenderingMode.alwaysTemplate)
            btn.setImage(img, for: .normal)
            btn.tintColor = .gray

wprowadź opis obrazu tutaj

Akbar Khan
źródło