Obtenga un color ligeramente más claro y más oscuro de UIColor


144

Estaba buscando poder convertir cualquier UIColor en un degradado. La forma en que tengo la intención de hacer esto es usando Core Graphics para dibujar un degradado. Lo que intento hacer es obtener un color, digamos:

[UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];

y obtener un color UIC que es un poco más oscuro y un poco más claro. ¿Alguien sabe como hacer esto? Gracias.


1
"degradado" implica que una parte de su imagen será un tono del color, mientras que otra parte será un tono más oscuro o más claro. ¿Es esta otra forma de definir lo que quieres hacer?
Michael Dautermann

2
Eche un vistazo a esta biblioteca github.com/yannickl/DynamicColor
onmyway133

Swift usando saturación de matiz y luminosidad stackoverflow.com/a/30012117/2303865
Leo Dabus

Respuestas:


279
- (UIColor *)lighterColorForColor:(UIColor *)c
{
    CGFloat r, g, b, a;
    if ([c getRed:&r green:&g blue:&b alpha:&a])
        return [UIColor colorWithRed:MIN(r + 0.2, 1.0)
                               green:MIN(g + 0.2, 1.0)
                                blue:MIN(b + 0.2, 1.0)
                               alpha:a];
    return nil;
}

- (UIColor *)darkerColorForColor:(UIColor *)c
{
    CGFloat r, g, b, a;
    if ([c getRed:&r green:&g blue:&b alpha:&a])
        return [UIColor colorWithRed:MAX(r - 0.2, 0.0)
                               green:MAX(g - 0.2, 0.0)
                                blue:MAX(b - 0.2, 0.0)
                               alpha:a];
    return nil;
}

Úselo así:

UIColor *baseColor = // however you obtain your color
UIColor *lighterColor = [self lighterColorForColor:baseColor];
UIColor *darkerColor = [self darkerColorForColor:baseColor];

EDITAR : como señaló @Anchu Chimala, para una máxima flexibilidad, estos métodos deben implementarse como una categoría UIColor. Además, según la idea de @ Riley, puede ser una mejor idea hacer que el color sea más oscuro o más claro en lugar de sumar o restar valores constantes. Como señaló @jrturton, no es necesario manipular los componentes RGB; Es mejor modificar la propiedad de brillo en sí. Considerándolo todo:

@implementation UIColor (LightAndDark)

- (UIColor *)lighterColor
{
    CGFloat h, s, b, a;
    if ([self getHue:&h saturation:&s brightness:&b alpha:&a])
        return [UIColor colorWithHue:h
                          saturation:s
                          brightness:MIN(b * 1.3, 1.0)
                               alpha:a];
    return nil;
}

- (UIColor *)darkerColor
{
    CGFloat h, s, b, a;
    if ([self getHue:&h saturation:&s brightness:&b alpha:&a])
        return [UIColor colorWithHue:h
                          saturation:s
                          brightness:b * 0.75
                               alpha:a];
    return nil;
}
@end

2
¿Por qué usar RGB cuando puede hacer lo mismo con matiz, saturación, brillo, y luego simplemente reconstruir el color con el brillo cambiado?
jrturton

2
He estado probando con la versión HSB, y no funcionó como se esperaba. A pesar de la getHue:saturation:brightnessfirma de UIKit, parece que funciona con HSV en lugar de HSB. Cambiar el brillo (de hecho, el valor) en este contexto no funcionará. Por ejemplo, el rojo puro (rgb (255,0,0)) tendrá un brillo / valor de 1, por lo que será imposible aligerar el brillo. Terminé trabajando con cambios en los valores RGB en su lugar.
rchampourlier

2
Creo que hay algo confuso en su respuesta: adopta los dos enfoques (uno con RGB y el otro con HSB para la versión de categoría), que no tienen el mismo resultado ... Y eso puede ser una cuestión de significado: aclarar es, de hecho, para mí hacer que el color tenga tendencia al blanco (oscurecerse al negro), mientras que cambiar el brillo / valor lo hace más fuerte (resp. más claro).
rchampourlier

44
Estos métodos fallarán si se usan en tonos grises. getHue:saturation:brightness:alphaVolverá FALSO.
Matthieu Riegler

2
Me pregunto por qué esta es la respuesta aceptada, ya que las dos soluciones sugeridas son incorrectas . Ambos simplemente cambian los valores teóricos que tienen poco que ver con lo que los seres humanos perciben como colores "más claros" o "más oscuros". Recomiendo leer esta gran publicación (shortlink: goo.gl/qqgk9V) para entender lo que quiero decir. Explica que el luminancevalor del espacio de color LAB es el verdadero que debe tener en cuenta al hacer que los colores sean más claros / oscuros. Vea mi respuesta sobre cómo puede usarlo y resolver este problema de la manera correcta.
Jeehut

59

TL; DR:

Rápido:

extension UIColor {

    var lighterColor: UIColor {
        return lighterColor(removeSaturation: 0.5, resultAlpha: -1)
    }

    func lighterColor(removeSaturation val: CGFloat, resultAlpha alpha: CGFloat) -> UIColor {
        var h: CGFloat = 0, s: CGFloat = 0
        var b: CGFloat = 0, a: CGFloat = 0

        guard getHue(&h, saturation: &s, brightness: &b, alpha: &a)
            else {return self}

        return UIColor(hue: h,
                       saturation: max(s - val, 0.0),
                       brightness: b,
                       alpha: alpha == -1 ? a : alpha)
    }
}

Uso:

let lightColor = somethingDark.lighterColor

C objetivo:

- (UIColor *)lighterColorRemoveSaturation:(CGFloat)removeS
                              resultAlpha:(CGFloat)alpha {
    CGFloat h,s,b,a;
    if ([self getHue:&h saturation:&s brightness:&b alpha:&a]) {
        return [UIColor colorWithHue:h
                          saturation:MAX(s - removeS, 0.0)
                          brightness:b
                               alpha:alpha == -1? a:alpha];
    }
    return nil;
}

- (UIColor *)lighterColor {
    return [self lighterColorRemoveSaturation:0.5
                                  resultAlpha:-1];
}

@rchampourlier tenía razón en su comentario a @ user529758 (La respuesta aceptada): las soluciones HSB (o HSV) y RGB dan resultados completamente diferentes. RGB solo agrega (o acerca el color) al blanco, y la solución HSB acerca el color al borde en la escala Brigtness, que básicamente comienza con el negro y termina con el color puro ...

Básicamente, el Brillo (Valor) hace que el color sea menos o más cercano al negro, mientras que Saturación lo hace menos o más cercano al blanco ...

Como se ve aquí:

Gráfico de color HSV

Entonces, la solución para hacer que un color sea realmente más brillante (es decir, más cercano al blanco ...) será hacer que su valor de Saturación sea más pequeño , dando como resultado esta solución:

- (UIColor *)lighterColor {
    CGFloat h,s,b,a;
    if ([self getHue:&h saturation:&s brightness:&b alpha:&a]) {
        return [UIColor colorWithHue:h
                          saturation:MAX(s - 0.3, 0.0)
                          brightness:b /*MIN(b * 1.3, 1.0)*/
                               alpha:a];
    }
    return nil;
}

En cuanto a hacer los colores más claros: obtuve el mejor resultado al reducir la saturación y aumentar el brillo / valor. Esta respuesta funciona muy bien para los colores "en la parte superior" del pastel en la imagen. Pero la mayoría de los colores caerán en algún lugar "dentro" del pastel. Acercarlos al blanco también requiere moverlos "hacia arriba" (valor creciente).
pejalo

Puede parecer que el valor creciente funciona, pero no será la funcionalidad "central" de "obtener un color más claro". De la misma manera, para obtener un color más oscuro, el enfoque correcto sería solo disminuir el "Valor" y dejar la Saturación como está ...
Aviel Gross

38

Extensión universal Swift para iOS y OS X , usando getHue :

#if os(OSX)

    import Cocoa
    public  typealias PXColor = NSColor

    #else

    import UIKit
    public  typealias PXColor = UIColor

#endif

    extension PXColor {

    func lighter(amount : CGFloat = 0.25) -> PXColor {
        return hueColorWithBrightnessAmount(1 + amount)
    }

    func darker(amount : CGFloat = 0.25) -> PXColor {
        return hueColorWithBrightnessAmount(1 - amount)
    }

    private func hueColorWithBrightnessAmount(amount: CGFloat) -> PXColor {
        var hue         : CGFloat = 0
        var saturation  : CGFloat = 0
        var brightness  : CGFloat = 0
        var alpha       : CGFloat = 0

        #if os(iOS)

            if getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
                return PXColor( hue: hue,
                                saturation: saturation,
                                brightness: brightness * amount,
                                alpha: alpha )
            } else {
                return self
            }

            #else

            getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
            return PXColor( hue: hue,
                            saturation: saturation,
                            brightness: brightness * amount,
                            alpha: alpha )

        #endif

    }

}

Uso:

let color = UIColor(red: 0.5, green: 0.8, blue: 0.8, alpha: 1.0)
color.lighter(amount:0.5)
color.darker(amount:0.5)

O (con los valores predeterminados):

color.lighter()
color.darker()

Muestra:

ingrese la descripción de la imagen aquí


Obtuve un error: -getHue: saturación: brillo: alfa: no válido para NSColor NSCalibratedWhiteColorSpace 0 1; primero necesita convertir el espacio de color.
Besi

2
Solucione agregando: let color = usingColorSpaceName(NSCalibratedRGBColorSpace) y luego reemplazando la getHuellamada con una llamada al color:color?.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
Oskar

Falta la función un _antes de la cantidad de palabras. Debería serprivate func hueColorWithBrightnessAmount(_ amount: CGFloat) -> PXColor {
Codemonk

Sugiero agregar la sugerencia de @Oskar a la respuesta. Esto solucionó un bloqueo en tiempo de ejecución para mí.
Besi

29

Solo quería dar el mismo resultado, en RGB, que

  • colocando el color con alfa x% sobre un fondo blanco para aclarar
  • colocando el color con alfa x% sobre un fondo negro para oscurecer

Lo que da el mismo resultado, AFAIK, que elegir el color en un degradado 'color a blanco' o 'color a negro', a x% del tamaño del degradado.

Para ese propósito, las matemáticas son simples:

extension UIColor {
    func mix(with color: UIColor, amount: CGFloat) -> UIColor {
        var red1: CGFloat = 0
        var green1: CGFloat = 0
        var blue1: CGFloat = 0
        var alpha1: CGFloat = 0

        var red2: CGFloat = 0
        var green2: CGFloat = 0
        var blue2: CGFloat = 0
        var alpha2: CGFloat = 0

        getRed(&red1, green: &green1, blue: &blue1, alpha: &alpha1)
        color.getRed(&red2, green: &green2, blue: &blue2, alpha: &alpha2)

        return UIColor(
            red: red1 * (1.0 - amount) + red2 * amount,
            green: green1 * (1.0 - amount) + green2 * amount,
            blue: blue1 * (1.0 - amount) + blue2 * amount,
            alpha: alpha1
        )
    }
}

Aquí hay ejemplos con algunos colores.

Ejemplos más oscuros y claros


Creo que esta es la mejor implementación sin recurrir a una biblioteca externa que implemente el espacio de color HSL
gog

24

La solución de user529758 en Swift:

Color más oscuro:

func darkerColorForColor(color: UIColor) -> UIColor {

       var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0

       if color.getRed(&r, green: &g, blue: &b, alpha: &a){
           return UIColor(red: max(r - 0.2, 0.0), green: max(g - 0.2, 0.0), blue: max(b - 0.2, 0.0), alpha: a)
       }

       return UIColor()
}

Color más claro:

func lighterColorForColor(color: UIColor) -> UIColor {

       var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0

       if color.getRed(&r, green: &g, blue: &b, alpha: &a){
           return UIColor(red: min(r + 0.2, 1.0), green: min(g + 0.2, 1.0), blue: min(b + 0.2, 1.0), alpha: a)
       }

       return UIColor()
}

1
Esta es una buena respuesta, excepto que sería más conveniente si se declarara como una extensión de la clase UIColor simplemente como var darker: UIColorpor ejemplo.
Bruno Philipe

13

Si convierte el color RGB al modelo de color HSL , puede variar el componente L = claridad de L = 0.0 (negro) sobre L = 0.5 (color natural) a L = 1.0 (blanco). UIColorno puede manejar HSL directamente, pero existen fórmulas para convertir RGB <-> HSL.



8
La diferencia entre el modelo de color HSL y HSB (a veces también llamado HSV) es que en HSB L = 1.0 corresponde al color puro, mientras que en HSL L = 1.0 corresponde al blanco y L = 0.5 al color puro. Como el póster original pedía, por ejemplo, una forma de hacer que el color azul (RGB = 0/0/1) sea más claro, creo que HSL es más flexible.
Martin R

6

Ninguna de las soluciones publicadas funcionó bastante para todos los colores y sombras, pero luego me topé con esta biblioteca que proporciona un conjunto de extensiones muy bien implementadas para UIColor.

Específicamente tiene una función de aclarado como parte de su implementación HSL: (UIColor *)lighten:(CGFloat)amount- que funciona perfectamente.


5

Sebyddd solución como una extensión:

extension UIColor {    
    func darker() -> UIColor {

        var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0

        if self.getRed(&r, green: &g, blue: &b, alpha: &a){
            return UIColor(red: max(r - 0.2, 0.0), green: max(g - 0.2, 0.0), blue: max(b - 0.2, 0.0), alpha: a)
        }

        return UIColor()
    }

    func lighter() -> UIColor {

        var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0

        if self.getRed(&r, green: &g, blue: &b, alpha: &a){
            return UIColor(red: min(r + 0.2, 1.0), green: min(g + 0.2, 1.0), blue: min(b + 0.2, 1.0), alpha: a)
        }

        return UIColor()
    }
}

Uso:

let darkerYellow = UIColor.yellow.darker()
let lighterYellow = UIColor.yellow.lighter()

2

Si desea que la solución user529758 funcione con sombras grises (como [UIColor lightGrayColor]o [UIColor darkGrayColor] tiene que mejorarla así:

- (UIColor *)lighterColor
{
    CGFloat h, s, b, a;
    if ([self getHue:&h saturation:&s brightness:&b alpha:&a]) {
        return [UIColor colorWithHue:h
                          saturation:s
                          brightness:MIN(b * 1.3, 1.0)
                               alpha:a];
    }

    CGFloat white, alpha;
    if ([self getWhite:&white alpha:&alpha]) {
        white = MIN(1.3*white, 1.0);
        return [UIColor colorWithWhite:white alpha:alpha];
    }

    return nil;
}

getHue:saturation:brightness:alphafalla (y regresa false) cuando se le solicita un tono gris, por lo tanto, deberá usarlo getWhite:alpha.


2

UIColor extensión y fijación encendedorColorForColor

extension UIColor {
  class func darkerColorForColor(color: UIColor) -> UIColor {
    var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0
    if color.getRed(&r, green: &g, blue: &b, alpha: &a){
      return UIColor(red: max(r - 0.2, 0.0), green: max(g - 0.2, 0.0), blue: max(b - 0.2, 0.0), alpha: a)
    }
    return UIColor()
  }

  class func lighterColorForColor(color: UIColor) -> UIColor {
    var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0
    if color.getRed(&r, green: &g, blue: &b, alpha: &a){
      let tmpColor = UIColor(red: min(r + 0.2, 1.0), green: min(g + 0.2, 1.0), blue: min(b + 0.2, 1.0), alpha: a)
      println(tmpColor)
      return tmpColor
    }
    return UIColor()
  }
}

2

Todas las demás respuestas en este hilo usan el sistema de color RGB o simplemente cambian el tono o el valor de brillo del sistema HSB . Como se explica en detalle en esta gran publicación de blog, la forma correcta de hacer que un color sea más claro o más oscuro es cambiar su luminancevalor. Ninguna de las otras respuestas hace eso. Si quieres hacerlo bien, usa mi solución o escribe la tuya después de leer la publicación del blog.


Desafortunadamente, es bastante complicado cambiar cualquiera de los atributos de un UIColor por defecto . Además, Apple ni siquiera admite ningún espacio de color basado en LAB como HCL en la UIColorclase ( Len LAB es el luminancevalor que estamos buscando).

El uso de HandyUIKit (instalarlo a través de Carthage) agrega soporte para HCL y hace su vida mucho más fácil :

import HandyUIKit    

let color = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0)

// create a new UIColor object with a specific luminance (slightly lighter)
color.change(.luminance, to: 0.7)

También hay una opción para aplicar un cambio relativo (recomendado):

// create a new UIColor object with slightly darker color
color.change(.luminance, by: -0.2)

Tenga en cuenta que HandyUIKit también agrega otras características útiles de interfaz de usuario en su proyecto: consulte su archivo README en GitHub para obtener más detalles.

¡Espero que ayude!


Para mi aplicación, dibujo rutas en mapas. Quería dibujar la ruta con una línea interior de un color con 100% de brillo y el borde con una variante más oscura de ese color. Debido a que la línea interna ya tenía un brillo del 100%, ninguno de los métodos funcionó. También cambiar la luminancia como se mencionó anteriormente no funcionó. Sin embargo, reducir el brillo con esta extensión muy agradable me sirvió. 'color.change (.brightness, por: -0.5)' Esta extensión es muy flexible en su uso y tiene muchas opciones. Recomiendo si, especialmente si tiene una aplicación con varios temas o paletas de colores.
guido

1

No estoy seguro de si está buscando algún tipo de respuesta Objective-C, pero en función de cómo funcionan los colores especificados por RGBA, creo que simplemente puede escalar los valores RGB según un factor arbitrario para obtener un "más claro" o sombra "más oscura". Por ejemplo, puede tener un azul:

[UIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:1.0];

¿Quieres un azul más oscuro? Multiplique los valores RGB por 0.9:

[UIColor colorWithRed:0.0 green:0.0 blue:0.9 alpha:1.0];

Voila O tal vez tienes una naranja:

[UIColor colorWithRed:1.0 green:0.4 blue:0.0 alpha:1.0];

Elija otro factor de escala, digamos, 0.8:

[UIColor colorWithRed:0.8 green:0.32 blue:0.0 alpha:1.0];

¿Es ese el tipo de efecto que estás buscando?


Ok, sí, eso es la mitad de lo que necesito. ¿Hay alguna manera de obtener un color más claro cuando el azul es 1 (Máx.)
CoreCode

@CoreCode no, en realidad no. A menos que desee cambiar el tamaño de la pantalla del dispositivo :) Vea mi respuesta.

1

Probado en Xcode 10 con Swift 4.x para iOS 12

Comience con su color como UIColor y elija un factor de oscurecimiento (como CGFloat)

let baseColor = UIColor.red
let darkenFactor: CGFloat = 2

El tipo CGColor tiene un valor opcional componentsque divide el color en RGBA (como una matriz CGFloat con valores entre 0 y 1). Luego puede reconstruir un UIColor utilizando valores RGBA tomados del CGColor y manipularlos.

let darkenedBase = UIColor(displayP3Red: startColor.cgColor.components![0] / darkenFactor, green: startColor.cgColor.components![1] / darkenFactor, blue: startColor.cgColor.components![2] / darkenFactor, alpha: 1)

En este ejemplo, cada uno de los valores RGB se dividió entre 2, haciendo que el color fuera la mitad de oscuro que antes. El valor alfa permaneció igual, pero también podría aplicar el factor de oscurecimiento en el valor alfa en lugar del RGB.


1

Swift 5

extension UIColor {

func lighter(by percentage:CGFloat=30.0) -> UIColor? {
    return self.adjust(by: abs(percentage) )
}

func darker(by percentage:CGFloat=30.0) -> UIColor? {
    return self.adjust(by: -1 * abs(percentage) )
}
func adjust(by percentage:CGFloat=30.0) -> UIColor? {
    var r:CGFloat=0, g:CGFloat=0, b:CGFloat=0, a:CGFloat=0;
    if(self.getRed(&r, green: &g, blue: &b, alpha: &a)){
        return UIColor(red: min(r + percentage/100, 1.0),
                       green: min(g + percentage/100, 1.0),
                       blue: min(b + percentage/100, 1.0),
                       alpha: a)
    }else{
        return nil


     }
    }
}

0

Idealmente, las funciones deberían estar encapsuladas dentro de una UIColorextensión llamada UIColor+Brightness.swift, y tener un brillo configurable; consulte el siguiente ejemplo:

import UIKit

extension UIColor {

  func lighterColorWithBrightnessFactor(brightnessFactor:CGFloat) -> UIColor {
    var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0
    if self.getRed(&r, green:&g, blue:&b, alpha:&a) {
      return UIColor(red:min(r + brightnessFactor, 1.0),
        green:min(g + brightnessFactor, 1.0),
        blue:min(b + brightnessFactor, 1.0),
        alpha:a)
    }
    return UIColor()
  }

}

0

Renderizo celdas de colores en función de un valor de estado:

imágenes de estado

Para esto, escribí una extensión rápida basada en un viejo código objc después de recibir un error al usar la sugerencia de CryingHippo:

extension UIColor{

    func darker(darker: CGFloat) -> UIColor{

        var red: CGFloat = 0.0
        var green: CGFloat = 0.0
        var blue: CGFloat = 0.0

        if self.colorSpace == UIColorSpace.genericGrayColorSpace(){

            red =  whiteComponent - darker
            green = whiteComponent - darker
            blue  = whiteComponent - darker
        } else {
            red = redComponent - darker
            green = greenComponent - darker
            blue = blueComponent - darker
        }

        if red < 0{
            green += red/2
            blue += red/2
        }

        if green < 0{
            red += green/2
            blue += green/2
        }

        if blue < 0{
            green += blue/2
            red += blue/2
        }

        return UIColor(
            calibratedRed: red,
            green: green,
            blue: blue,
            alpha: alphaComponent
        )
    }

    func lighter(lighter: CGFloat) -> UIColor{
        return darker(-lighter)
    }
}

Lo mismo funciona NSColortambién. Simplemente reemplace UIColorcon NSColor.


0

Aquí hay una categoría UIColor que también permite controlar la cantidad de cambio de color.

- (UIColor *)lighterColorWithDelta:(CGFloat)delta
{
    CGFloat r, g, b, a;
    if ([self getRed:&r green:&g blue:&b alpha:&a])
        return [UIColor colorWithRed:MIN(r + delta, 1.0)
                               green:MIN(g + delta, 1.0)
                                blue:MIN(b + delta, 1.0)
                               alpha:a];
    return nil;
}

- (UIColor *)darkerColorWithDelta:(CGFloat)delta
{
    CGFloat r, g, b, a;
    if ([self getRed:&r green:&g blue:&b alpha:&a])
        return [UIColor colorWithRed:MAX(r - delta, 0.0)
                               green:MAX(g - delta, 0.0)
                                blue:MAX(b - delta, 0.0)
                               alpha:a];
    return nil;
}

0

Una extensión Swift basada en la respuesta @Sebyddd:

import Foundation
import UIKit

extension UIColor{
    func colorWith(brightness: CGFloat) -> UIColor{
        var r:CGFloat = 0, g:CGFloat = 0, b:CGFloat = 0, a:CGFloat = 0

        if getRed(&r, green: &g, blue: &b, alpha: &a){
            return UIColor(red: max(r + brightness, 0.0), green: max(g + brightness, 0.0), blue: max(b + brightness, 0.0), alpha: a)
        }

        return UIColor()
    }
}

-1

para un color más oscuro, este es el más simple: theColor = [theColor shadowWithLevel: s]; //s:0.0 a 1.0


Este método solo está disponible en NSColor, no en UIColor.
Seguido el
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.