基于进度值的UIColor过渡

27

我有一个进度条,想要给它分配一个自定义颜色,并根据进度从一个颜色渐变到另一个颜色。使用下面的方法,我得到了一个包括红色、深棕色和深绿色在内的深彩虹效果的颜色。起始颜色将是浅蓝色,目标颜色为浅绿色。

-(UIColor *) makeCustomColorFromProgressValue:(float) progress{


UIColor *color;

// startColor Color - lightBlue
float red = 0.53;
float green = 0.82;
float blue = 1;

//Destination Color - lightGreen
float finalRed = 0.53;
float finalGreen = 1;
float finalBlue = 0.82;

float newRed = 80;//finalRed *255;
float newGreen = (finalGreen *progress) *255;
float newBlue = (finalBlue *progress) *255;
color = Rgb2UIColor(newRed, newGreen, newBlue);



return color;
}

Swift - 3.0 && 4.0 点击这里 - ramchandra n
7个回答

29
您可以在颜色之间进行“线性插值”:
CGFloat newRed   = (1.0 - progress) * red   + progress * finalRed;
CGFloat newGreen = (1.0 - progress) * green + progress * finalGreen;
CGFloat newBlue  = (1.0 - progress) * blue  + progress * finalBlue;
UIColor *color = [UIColor colorWithRed:newRed green:newGreen blue:newBlue alpha:1.0];

这提供了progress == 0的初始颜色和progress == 1的最终颜色。


我最终想到了另一种方法... float newGreen = (((finalGreen -green) *progress) +green) *255; 我会点赞并感谢您的回复。 - snksnk
@snksnk:不客气(请注意,您的公式和我的实际上是相同的)。 - Martin R
我注意到它们达到了相同的结果。我会将您的答案标记为正确的,因为您值得这样做:) 以备将来考虑,这是我也使用的宏.. #define Rgb2UIColor(r, g, b) [UIColor colorWithRed:((r) / 255.0) green:((g) / 255.0) blue:((b) / 255.0) alpha:1] - snksnk

26

Jonathan Ellis的代码转换而来的Swift版本

extension UIColor {
    func interpolateRGBColorTo(_ end: UIColor, fraction: CGFloat) -> UIColor? {
        let f = min(max(0, fraction), 1)

        guard let c1 = self.cgColor.components, let c2 = end.cgColor.components else { return nil }

        let r: CGFloat = CGFloat(c1[0] + (c2[0] - c1[0]) * f)
        let g: CGFloat = CGFloat(c1[1] + (c2[1] - c1[1]) * f)
        let b: CGFloat = CGFloat(c1[2] + (c2[2] - c1[2]) * f)
        let a: CGFloat = CGFloat(c1[3] + (c2[3] - c1[3]) * f)

        return UIColor(red: r, green: g, blue: b, alpha: a)
    }
}

let color1 = UIColor(red: 0.035, green: 0.216, blue: 0.933, alpha: 1.00)
let color2 = UIColor(red: 0.933, green: 0.794, blue: 0.000, alpha: 1.00)

color1.interpolateRGBColorTo(color2, fraction:0.1)

UIColor Interpolation


第二行应该是 f = min(1, f),我认为是这样的,对吧? - Mikkel Selsøe
3
使用以下方式创建的颜色 UIColor(white: 0.0, alpha: 0.4) 会导致崩溃。 - Mihai Fratu
创建一个RGB UIColor:UIColor(red:1.0, green:1.0, blue:1.0, alpha: 0.4) 或使用此版本:https://dev59.com/cmAh5IYBdhLWcg3wDfuy#35853850 - Atika
6
请注意,不是每个 UIColor 都有 4 个组件。如果颜色是这样创建的:UIColor(white: 101.0 / 255.0, alpha: 1.0),那么这种颜色只有 2 个组件,使用时会导致崩溃。 - Vladimir

19

上述提供的Swift版本不能与白色一起使用,这里是解决方案:

struct ColorComponents {
    var r:CGFloat, g:CGFloat, b:CGFloat, a:CGFloat
}

extension UIColor {

    func getComponents() -> ColorComponents {
        if (cgColor.numberOfComponents == 2) {
          let cc = cgColor.components!
          return ColorComponents(r:cc[0], g:cc[0], b:cc[0], a:cc[1])
        }
        else {
          let cc = cgColor.components!
          return ColorComponents(r:cc[0], g:cc[1], b:cc[2], a:cc[3])
        }
    }

    func interpolateRGBColorTo(end: UIColor, fraction: CGFloat) -> UIColor {
        var f = max(0, fraction)
        f = min(1, fraction)

        let c1 = self.getComponents()
        let c2 = end.getComponents()

        let r = c1.r + (c2.r - c1.r) * f
        let g = c1.g + (c2.g - c1.g) * f
        let b = c1.b + (c2.b - c1.b) * f
        let a = c1.a + (c2.a - c1.a) * f

        return UIColor.init(red: r, green: g, blue: b, alpha: a)
    }

}

let color1 = UIColor.whiteColor()
let color2 = UIColor(red: 0.933, green: 0.794, blue: 0.000, alpha: 1.00)

color1.interpolateRGBColorTo(color2, fraction:0.1)
color1.interpolateRGBColorTo(color2, fraction:0.2)
color1.interpolateRGBColorTo(color2, fraction:0.3)

❤︎ Playground


15

这里是一个适用于UIColor的类别,它可以用来在RGB或HSV中线性插值两个UIColor

@implementation UIColor (Interpolate)

+ (UIColor *)interpolateRGBColorFrom:(UIColor *)start to:(UIColor *)end withFraction:(float)f {

    f = MAX(0, f);
    f = MIN(1, f);

    const CGFloat *c1 = CGColorGetComponents(start.CGColor);
    const CGFloat *c2 = CGColorGetComponents(end.CGColor);

    CGFloat r = c1[0] + (c2[0] - c1[0]) * f;
    CGFloat g = c1[1] + (c2[1] - c1[1]) * f;
    CGFloat b = c1[2] + (c2[2] - c1[2]) * f;
    CGFloat a = c1[3] + (c2[3] - c1[3]) * f;

    return [UIColor colorWithRed:r green:g blue:b alpha:a];
}

+ (UIColor *)interpolateHSVColorFrom:(UIColor *)start to:(UIColor *)end withFraction:(float)f {

    f = MAX(0, f);
    f = MIN(1, f);

    CGFloat h1,s1,v1,a1;
    [start getHue:&h1 saturation:&s1 brightness:&v1 alpha:&a1];

    CGFloat h2,s2,v2,a2;
    [end getHue:&h2 saturation:&s2 brightness:&v2 alpha:&a2];

    CGFloat h = h1 + (h2 - h1) * f;
    CGFloat s = s1 + (s2 - s1) * f;
    CGFloat v = v1 + (v2 - v1) * f;
    CGFloat a = a1 + (a2 - a1) * f;

    return [UIColor colorWithHue:h saturation:s brightness:v alpha:a];
}

@end

一个好的解决方案,谢谢!但是,使用RGB内插法的颜色,最初是用HSV模型创建的,它是否会起作用?由于我们可以访问内部存储,所以可能存在HSV格式的值...我的假设错了吗? - Cemen
是的,您应该能够使用由RGB创建的UIColor插值HSV,并使用由HSV创建的UIColor插值RGB。 - Jonathan Ellis

9
另一个针对Swift的端口,作为UIColor的扩展。这次包括两个插值函数。
extension UIColor {
    func interpolateRGBColorTo(end:UIColor, fraction:CGFloat) -> UIColor {
        var f = max(0, fraction)
        f = min(1, fraction)
        let c1 = CGColorGetComponents(self.CGColor)
        let c2 = CGColorGetComponents(end.CGColor)
        let r: CGFloat = CGFloat(c1[0] + (c2[0] - c1[0]) * f)
        let g: CGFloat = CGFloat(c1[1] + (c2[1] - c1[1]) * f)
        let b: CGFloat = CGFloat(c1[2] + (c2[2] - c1[2]) * f)
        let a: CGFloat = CGFloat(c1[3] + (c2[3] - c1[3]) * f)
        return UIColor.init(red:r, green:g, blue:b, alpha:a)
    }
    

    func interpolateHSVColorFrom(end: UIColor, fraction: CGFloat) -> UIColor {
        var f = max(0, fraction)
        f = min(1, fraction)
        var h1: CGFloat = 0, s1: CGFloat = 0, b1: CGFloat = 0, a1: CGFloat = 0
        self.getHue(&h1, saturation: &s1, brightness: &b1, alpha: &a1)
        var h2: CGFloat = 0, s2: CGFloat = 0, b2: CGFloat = 0, a2: CGFloat = 0
        end.getHue(&h2, saturation: &s2, brightness: &b2, alpha: &a2)
        let h = h1 + (h2 - h1) * f
        let s = s1 + (s2 - s1) * f
        let b = b1 + (b2 - b1) * f
        let a = a1 + (a2 - a1) * f
        return UIColor(hue: h, saturation: s, brightness: b, alpha: a)
    }
}

编辑:已修复@protuberian发现的错误,感谢。

待办事项:颜色线性插值将无法工作,需要使用更曲线的插值方法。


1
你在 HSB 插值中犯了一个错误,应该使用 s1 而不是 b1,正确的代码为 let s = s1 + (s2 - s1) * f。 - protuberian

2
这里是一个使用便利初始化程序并添加返回中间颜色数组的函数的Swift 3版本。
extension UIColor {
convenience init?(interpolatedFrom fromColor: UIColor, to toColor: UIColor, byFraction fraction: CGFloat) {
    guard fromColor.cgColor.numberOfComponents >= 4 && toColor.cgColor.numberOfComponents >= 3 else {
        print("Color interpolation requires both the to and from color to be provided with components for red, green, blue, and alpha.")
        return nil
    }

    var fraction = max(0, fraction)
    fraction = min(1, fraction)

    guard let fromComponents = fromColor.cgColor.components, let toComponents = toColor.cgColor.components else {
        print("Unable to extract components from colors provided for interpolation.")
        return nil
    }

    let red: CGFloat = CGFloat(fromComponents[0] + (toComponents[0] - fromComponents[0]) * fraction)
    let green: CGFloat = CGFloat(fromComponents[1] + (toComponents[1] - fromComponents[1]) * fraction)
    let blue: CGFloat = CGFloat(fromComponents[2] + (toComponents[2] - fromComponents[2]) * fraction)
    let alpha: CGFloat = CGFloat(fromComponents[3] + (toComponents[3] - fromComponents[3]) * fraction)
    self.init(red:red, green:green, blue:blue, alpha:alpha)
}

class func interpolateColors(from fromColor: UIColor, to toColor: UIColor, interpolations: Int) -> [UIColor] {
    guard interpolations > 2 else { return [fromColor, toColor] }

    let increment = CGFloat(1) / CGFloat(interpolations - 1)
    var result = [UIColor]()
    for i in 0..<interpolations {
        let fraction = CGFloat(i) * increment
        guard let color = UIColor(interpolatedFrom: fromColor, to: toColor, byFraction: fraction) else {
            print("Unable to create an interpolated color for fraction \(fraction). Will use gray instead.")
            result.append(.gray)
            continue
        }
        result.append(color)
    }

    return result
}
}

使用方法:

let color1 = UIColor(red: 0.137, green: 0.157, blue: 0.196, alpha: 1)
let color2 = UIColor(red: 0.455, green: 0.475, blue: 0.525, alpha: 1)
let interpolatedColors = UIColor.interpolateColors(from: color1, to: color2, interpolations: 5)

输出:

[
r 0.137 g 0.157 b 0.196 a 1.0,
r 0.216 g 0.236 b 0.278 a 1.0,
r 0.296 g 0.316 b 0.361 a 1.0, 
r 0.375 g 0.396 b 0.443 a 1.0,
r 0.455 g 0.475 b 0.525 a 1.0
]

2

SwiftUI

extension Color {
    
    var components: (r: Double, g: Double, b: Double, o: Double)? {
        let uiColor: UIColor
        
        var r: CGFloat = 0
        var g: CGFloat = 0
        var b: CGFloat = 0
        var o: CGFloat = 0
        
        if self.description.contains("NamedColor") {
            let lowerBound = self.description.range(of: "name: \"")!.upperBound
            let upperBound = self.description.range(of: "\", bundle")!.lowerBound
            let assetsName = String(self.description[lowerBound..<upperBound])
            
            uiColor = UIColor(named: assetsName)!
        } else {
            uiColor = UIColor(self)
        }

        guard uiColor.getRed(&r, green: &g, blue: &b, alpha: &o) else { return nil }
        
        return (Double(r), Double(g), Double(b), Double(o))
    }
    
    
    func interpolateTo(color: Color, fraction: Double) -> Color {
        let s = self.components!
        let t = color.components!
        
        let r: Double = s.r + (t.r - s.r) * fraction
        let g: Double = s.g + (t.g - s.g) * fraction
        let b: Double = s.b + (t.b - s.b) * fraction
        let o: Double = s.o + (t.o - s.o) * fraction
        
        return Color(red: r, green: g, blue: b, opacity: o)
    }
}

这是一个使用Color而非UIColorSwiftUI工作示例。 请注意,我已考虑了具有浅色和深色外观的资源颜色。关于为什么我们必须使用UIColor(named:)初始化器的详细解释可以在这里找到。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接