如何使用分色创建渐变效果?

How can I create a gradient effect using color divisions?

我正在尝试创建一个 table 视图(自定义 class 而不是 UITableView),其结果具有渐变效果,如下面的示例图片所示:

我尝试了什么:

  1. 我成功地将正确的渐变添加为每个 table 单元格的背景,但我需要它是每个标签文本的颜色,而不是每个单元格的背景。 。 (我问的是这个。)

尝试失败:

  1. 创建自定义渐变图像并将其作为 colorWithPatternImage: 添加到每个标签,但由于渐变是一个,所以每个单元格看起来都一样。 Question.

尝试失败:

最后要尝试的事情:

假设您有两种颜色,color1 和 color2。混合这些颜色可以产生渐变。在上图中,color1 = 紫色,color2 = 橙色。根据结果​​的数量将渐变分成几个部分,然后找到每个部分的平均颜色,并将其用作每个相应结果的文本颜色,可以很容易地创建渐变效果。

例如:

5 个结果 = 5 个部门。

结果没有那么详细,因为每个文本都是纯色,但当文本很小时同样令人印象深刻:

问题是,对于像这样的两种颜色:

紫色:128.0、0.0、255.0

橙色:255.0、128.0、0.0

如何将它分成 5 个部分并求出每个部分的平均值?

我可以使用 pixelmator 中的吸管工具来完成此操作,但前提是我知道结果的固定数量,不会使用 6 个结果。

我无法用数学来解决它,我不知道从哪里开始。

有什么想法吗?

好吧,下面是如何在最后做纯色数学部分:

如果你有 n + 1 种颜色,对于 0 <= m <= n:

color.red[m] = (purple.red * (m/n)) + (orange.red * ((n-m)/n);
color.green[m] = (purple.green * (m/n)) + (orange.green * ((n-m)/n));
color.blue[m] = (purple.blue * (m/n)) + (orange.blue * ((n-m)/n));

希望对您有所帮助。

您可以对颜色的 rgb 值使用数学运算。

要获取 rgb 值,您可以在 UIColor 上使用 getRed:green:blue:alpha 方法。然后你所要做的就是根据你需要的部分来平均颜色。

这是一个函数,它应该 return 基于开始和结束颜色的颜色数组,以及您需要多少个分区。

解决方案

func divideColors(firstColor: UIColor, secondColor: UIColor, sections: Int) -> [UIColor] {

    // get rgb values from the colors
    var firstRed: CGFloat = 0; var firstGreen: CGFloat = 0; var firstBlue: CGFloat = 0; var firstAlpha: CGFloat = 0;
    firstColor.getRed(&firstRed, green: &firstGreen, blue: &firstBlue, alpha: &firstAlpha)
    var secondRed: CGFloat = 0; var secondGreen: CGFloat = 0; var secondBlue: CGFloat = 0; var secondAlpha: CGFloat = 0;
    secondColor.getRed(&secondRed, green: &secondGreen, blue: &secondBlue, alpha: &secondAlpha)

    // function to mix the colors
    func mix(_ first: CGFloat, _ second: CGFloat, ratio: CGFloat) -> CGFloat { return first + ratio * (second - first) }

    // variable setup
    var colors = [UIColor]()
    let ratioPerSection = 1.0 / CGFloat(sections)

    // mix the colors for each section
    for section in 0 ..< sections {
        let newRed = mix(firstRed, secondRed, ratio: ratioPerSection * CGFloat(section))
        let newGreen = mix(firstGreen, secondGreen, ratio: ratioPerSection * CGFloat(section))
        let newBlue = mix(firstBlue, secondBlue, ratio: ratioPerSection * CGFloat(section))
        let newAlpha = mix(firstAlpha, secondAlpha, ratio: ratioPerSection * CGFloat(section))
        let newColor = UIColor(red: newRed, green: newGreen, blue: newBlue, alpha: newAlpha)
        colors.append(newColor)
    }

    return colors

}

您的问题被标记为 Objective-C,但将上面的 Swift 代码转换为 Objective-C 应该很容易,因为您将使用相同的 UIColor API.

下面是一些用于测试上述功能的代码(非常适合 Swift 游乐场)。

测试代码

let sections = 5
let view = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 50))
for (index, color) in divideColors(firstColor: UIColor.purple, secondColor: UIColor.orange, sections: sections).enumerated() {
    let v = UIView(frame: CGRect(x: CGFloat(index) * 250 / CGFloat(sections), y: 0, width: 250 / CGFloat(sections), height: 50))
    v.backgroundColor = color
    view.addSubview(v)
}
view.backgroundColor = .white

测试结果

它也适用于任意数量的部分和不同的颜色!

所以你想要这个:

您可以通过多种方式实现这一点。这是一种像素完美的方式(它通过标签绘制渐变,而不是使每个标签都是纯色)。

创建 UILabel 的子类。在您的子类中,覆盖 drawTextInRect: 以绘制渐变。

让我们这样声明子类:

IB_DESIGNABLE
@interface GradientLabel: UILabel

@property (nonatomic, weak) IBOutlet UIView *gradientCoverageView;
@property (nonatomic, strong) IBInspectable UIColor *startColor;
@property (nonatomic, strong) IBInspectable UIColor *endColor;

@end

gradientCoverageView 出口连接到覆盖整个渐变区域的视图 - 因此,一个覆盖所有五个标签的视图。这可能是标签的超级视图,或者它可能只是您设置为(不可见地)填充同一区域的隐藏视图。

startColor 设置为紫色,将 endColor 设置为橙色。

我们将分三步绘制渐变填充文本(在我们的 drawTextInRect: 覆盖中):

  1. 调用super以正常绘制文本。

  2. 设置图形上下文混合模式kCGBlendModeSourceIn。这种混合模式告诉 Core Graphics 只在上下文已经绘制的地方绘制,并覆盖那里绘制的任何内容。因此 Core Graphics 将 super 绘制的文本视为掩码。

  3. 绘制渐变,起点在 coverage 视图的顶部,终点在 coverage[] 的底部=66=]视图,不是在当前标签的顶部和底部。因此,渐变跨越整个覆盖视图,但被屏蔽以仅显示在步骤 1 中绘制当前标签文本的位置。

实现如下:

@implementation GradientLabel

- (void)drawTextInRect:(CGRect)rect {
    [super drawTextInRect:rect];

    CGContextRef gc = UIGraphicsGetCurrentContext();
    NSArray *colors = @[(__bridge id)self.startColor.CGColor, (__bridge id)self.endColor.CGColor];
    CGGradientRef gradient = CGGradientCreateWithColors(CGBitmapContextGetColorSpace(gc), (__bridge CFArrayRef)colors, NULL);

    UIView *coverageView = self.gradientCoverageView ?: self;
    CGRect coverageRect = [coverageView convertRect:coverageView.bounds toView:self];
    CGPoint startPoint = coverageRect.origin;
    CGPoint endPoint = { coverageRect.origin.x, CGRectGetMaxY(coverageRect) };

    CGContextSaveGState(gc); {
        CGContextSetBlendMode(gc, kCGBlendModeSourceIn);
        CGContextDrawLinearGradient(gc, gradient, startPoint, endPoint, 0);
    } CGContextRestoreGState(gc);

    CGGradientRelease(gradient);
}

@end

这是我故事板中的布局:

对于所有五个 GradientLabel 实例,我将 startColor 设置为紫色,将 endColor 设置为橙色,并将 gradientCoverageView 插座连接到封闭堆栈查看。

对于 Xamarin iOS C#

public static List<UIColor> DividedColors(UIColor firstColor, UIColor secondColor, int sections)
{
    nfloat firstRed = 0;
    nfloat firstGreen = 0;
    nfloat firstBlue = 0;
    nfloat firstAlpha = 0;
    firstColor.GetRGBA(out firstRed, out firstGreen, out firstBlue, out firstAlpha);

    nfloat secondRed = 0;
    nfloat secondGreen = 0;
    nfloat secondBlue = 0;
    nfloat secondAlpha = 0;
    secondColor.GetRGBA(out secondRed, out secondGreen, out secondBlue, out secondAlpha);

    nfloat Mix(nfloat first, nfloat second, nfloat ratio)
    {
        return first + ratio * (second - first);
    }

    List<UIColor> colors = new List<UIColor>();
    var ratioPerSection = 1.0f / sections;

    for (int i = 0; i < sections; i++)
    {
        var newRed = Mix(firstRed, secondRed, ratioPerSection * i);
        var newGreen = Mix(firstGreen, secondGreen, ratioPerSection * i);
        var newBlue = Mix(firstBlue, secondBlue, ratioPerSection * i);
        var newAlpha = Mix(firstAlpha, secondAlpha, ratioPerSection * i);
        var newColor = new UIColor(newRed, newGreen, newBlue, newAlpha);
        colors.Add(newColor);
    }

    return colors;
}