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

时间:2017-09-01 16:05:02

标签: ios objective-c calayer uicolor cagradientlayer

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

enter image description here

我尝试了什么:

  1. 我成功地设法将正确的渐变作为背景添加到每个表格单元格,但我需要它是每个标签的文本颜色而不是每个单元格的背景。 Question。 (我问过这个。)
  2. FAILED ATEMPT:

    enter image description here

    1. 创建自定义渐变图像并将其作为colorWithPatternImage:添加到每个标签但由于渐变为1,因此每个单元格看起来都相同。 Question
    2. FAILED ATEMPT:

      enter image description here

      最后要尝试的事情:

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

      例如:

      5个结果= 5个分区。

      • division1 = purple
      • division2 =少紫色,更橙色
      • division3 =等于紫色,等于橙色
      • division4 =最少紫色,最橙色
      • division5 = orange

      结果并不详细,因为每个文字都是纯色,但是当文字较小时同样令人印象深刻:

      enter image description here

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

      紫色:128.0,0.0,255.0

      橙色:255.0,128.0,0.0

      如何将其划分为5个部分并找到每个部分的平均值?

      我可以使用pixelmator中的吸管工具来做到这一点,但只有当我知道固定数量的结果时,才能使用6个结果。

      我不能用数学来处理它,我不知道从哪里开始。

      有什么想法吗?

4 个答案:

答案 0 :(得分:1)

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

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

这是一个函数,它应该根据开始和结束颜色返回一个颜色数组,以及你需要多少个分区。

<强>解决方案

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

测试结果

An image of the five colors, evenly divided between purple and orange

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

An image of more colors, evenly divided between red and yellow

答案 1 :(得分:1)

所以你想要这个:

screenshot

有几种方法可以实现这一点。这是一种像素完美的方式(它通过标签绘制渐变而不是使每个标签成为纯色)。

创建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 视图底部的终点绘制渐变, not 位于当前标签的顶部和底部。因此,渐变跨越整个覆盖范围视图,但被遮盖以仅显示在步骤1中绘制当前标签的文本的位置。

  4. 以下是实施:

    @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
    

    这是我的故事板中的布局:

    storyboard

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

答案 2 :(得分:0)

嗯,这里是如何在最后做纯色浊位:

如果您有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));

希望这有帮助。

答案 3 :(得分:0)

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