【问题标题】:How to create an overlay color with a gradient in iOS如何在 iOS 中使用渐变创建叠加颜色
【发布时间】:2016-04-18 13:28:03
【问题描述】:

背景信息:我有一个UIImageView。我通过以下方式在其图像上添加了叠加颜色:

UIGraphicsBeginImageContext(initialImage.size);
[initialImage drawInRect:CGRectMake(0, 0, initialImage.size.width, initialImage.size.height) blendMode:kCGBlendModeNormal alpha:alphaValue];
UIBezierPath * path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, initialImage.size.width, initialImage.size.height)];
[overlayColor setFill];
[path fillWithBlendMode:kCGBlendModeMultiply alpha:1];
finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

[self setImage:finalImage];

我仍然想将其添加为叠加颜色,但我希望它具有渐变色。我一直在想办法做到这一点,但并没有真正成功。我想,添加带有渐变的叠加颜色的方法是错误的?我不确定如何做到这一点。我尝试将CGGradientLayer 作为sublayer 添加到UIImageView,但它不起作用。

我考虑添加一个UIView 并将其设置为backgroundColoroverlayColor,然后添加一个CGGradientLayer 作为UIViewsublayer,它作为subview 添加到@987654333 @ 但是,我们不应该将subviews 添加到UIImageViews

有人可以帮我解决这个问题吗?也许我应该改变我的方法?

为我指明正确的方向也很棒!

如果这篇文章没有完全清楚,我期待您的回复和道歉!

提前感谢您的帮助!

编辑:CGGradientLayer 的代码

CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = self.frame;

UIColor *colorOne = [UIColor colorFromHex:self.feedColor withAlpha:(alphaValue * 0.7)];
UIColor *colorTwo = [UIColor colorFromHex:self.feedColor withAlpha:(alphaValue * 1.0)];

gradient.colors = [NSArray arrayWithObjects:(id)colorOne.CGColor, (id)colorTwo.CGColor, nil];

[self.layer insertSublayer:gradient atIndex:0];

【问题讨论】:

  • 您是否在UIImageView 层上方插入了CGGradientLayer
  • 是的,我刚刚编辑了我的帖子以显示代码。希望这就是您要问的!

标签: ios objective-c iphone uiimageview cgcolor


【解决方案1】:

我会在这个question发布协作解决方案

此类别允许您使用任何混合模式(正片叠底、正常等)和 CoreGraphics 添加颜色叠加或渐变颜色叠加

斯威夫特 4:

extension UIImage {

    //creates a static image with a color of the requested size
    static func fromColor(color: UIColor, size: CGSize) -> UIImage {
        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()
        context?.setFillColor(color.cgColor)
        context?.fill(rect)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img!
    }

    func blendWithColorAndRect(blendMode: CGBlendMode, color: UIColor, rect: CGRect) -> UIImage {

        let imageColor = UIImage.fromColor(color: color, size:self.size)

        let rectImage = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)

        UIGraphicsBeginImageContextWithOptions(self.size, true, 0)
        let context = UIGraphicsGetCurrentContext()

        // fill the background with white so that translucent colors get lighter
        context!.setFillColor(UIColor.white.cgColor)
        context!.fill(rectImage)

        self.draw(in: rectImage, blendMode: .normal, alpha: 1)
        imageColor.draw(in: rect, blendMode: blendMode, alpha: 0.8)

        // grab the finished image and return it
        let result = UIGraphicsGetImageFromCurrentImageContext()

        //self.backgroundImageView.image = result
        UIGraphicsEndImageContext()
        return result!

    }
    //creates a static image with a gradient of colors of the requested size
    static func fromGradient(colors: [UIColor], locations: [CGFloat], horizontal: Bool, size: CGSize) -> UIImage {
        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)

        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()

        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let cgColors = colors.map {$0.cgColor} as CFArray
        let grad = CGGradient(colorsSpace: colorSpace, colors: cgColors , locations: locations)

        let startPoint = CGPoint(x: 0, y: 0)
        let endPoint = horizontal ? CGPoint(x: size.width, y: 0) : CGPoint(x: 0, y: size.height)

        context?.drawLinearGradient(grad!, start: startPoint, end: endPoint, options: .drawsAfterEndLocation)

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

    func blendWithGradientAndRect(blendMode: CGBlendMode, colors: [UIColor], locations: [CGFloat], horizontal: Bool = false, alpha: CGFloat = 1.0, rect: CGRect) -> UIImage {

        let imageColor = UIImage.fromGradient(colors: colors, locations: locations, horizontal: horizontal, size: size)

        let rectImage = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)

        UIGraphicsBeginImageContextWithOptions(self.size, true, 0)
        let context = UIGraphicsGetCurrentContext()

        // fill the background with white so that translucent colors get lighter
        context!.setFillColor(UIColor.white.cgColor)
        context!.fill(rectImage)

        self.draw(in: rectImage, blendMode: .normal, alpha: 1)
        imageColor.draw(in: rect, blendMode: blendMode, alpha: alpha)

        // grab the finished image and return it
        let result = UIGraphicsGetImageFromCurrentImageContext()

        //self.backgroundImageView.image = result
        UIGraphicsEndImageContext()
        return result!

    }
}

渐变示例:

let  newImage = image.blendWithGradientAndRect(blendMode: .multiply,
                                           colors: [.red, .white],
                                           locations: [0, 1],
                                           horizontal: true,
                                           alpha: 0.8,
                                           rect: imageRect)

单色示例:

let newImage = image.blendWithColorAndRect(blendMode: .multiply, color: .red, rect: imageRect)

【讨论】:

  • 有点晚了,但我正在使用您的部分代码。我遇到的问题是我目前在tableView(_:willDisplay:forRowAt:) 期间在自定义tableviewcell 上使用它,这使得滚动非常跳跃。因为我的单元格是动态的,所以我需要重新绘制它们,但我不知道是否有更好的方法来实现这一点,而无需每次都重新绘制图像。有什么想法吗?谢谢
【解决方案2】:

我会使用 Core Graphics 来获取您的输入图像,对其应用渐变叠加,然后将其传递给 UIImageView。这样的事情应该可以达到预期的效果:

- (UIImage *)imageWithGradientOverlay:(UIImage *)sourceImage color1:(UIColor *)color1 color2:(UIColor *)color2 gradPointA:(CGPoint)pointA gradPointB:(CGPoint)pointB {

    CGSize size = sourceImage.size;

    // Start context
    UIGraphicsBeginImageContext(size);
    CGContextRef c = UIGraphicsGetCurrentContext();

    // Draw source image into context
    CGContextDrawImage(c, (CGRect){CGPointZero, size}, sourceImage.CGImage);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat gradLocs[] = {0, 1};
    NSArray *colors = @[(id)color1.CGColor, (id)color2.CGColor];

    // Create a simple linear gradient with the colors provided.
    CGGradientRef grad = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, gradLocs);
    CGColorSpaceRelease(colorSpace);

    // Draw gradient with multiply blend mode over the source image
    CGContextSetBlendMode(c, kCGBlendModeMultiply);
    CGContextDrawLinearGradient(c, grad, pointA, pointB, 0);
    CGGradientRelease(grad);

    // Grab resulting image from context
    UIImage *resultImg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return resultImg;
}

这里sourceImage是输入图像,color1color2是你的渐变颜色,gradPointAgradPointB是你的线性渐变端点(在Core Graphics坐标系中,左下角是( 0,0))。

这样您就不必再弄乱图层了。如果您经常用不同的颜色重新绘制,那么您可能需要采用使用图层的方法。

【讨论】:

  • 感谢您的回复。我不断地改变颜色和图像。你会说我使用的 CGGradientLayer 代码是正确的吗?我应该直接使用它而不进行overlayColor处理吗?
  • 对我来说看起来是正确的,但从stackoverflow.com/questions/8630869/… 看来,您似乎也无法将子层添加到UIImageView。您可能必须将其添加到超级视图的图层中。
  • 我也想知道,你为什么使用UIImageView?为什么不使用CALayer 并将contents 属性设置为您的图像?这样您就可以轻松地在其上叠加一个子图层。
  • 我实际上是 UIImageView 的子类。您认为将其创建为 CALayer 的子类更好吗?
  • 我愿意,因为它会消除 UIImageView 带来的怪癖。毕竟UIImageView 只是底层CALayer 的容器,内容为图像。
【解决方案3】:

如果您正在寻找更动态的方法,那么我将继承 CALayer 而不是 UIImageView。因此你会想要这样的东西:

@interface gradientImageLayer : CALayer

- (instancetype)initWithFrame:(CGRect)frame;

@end

@implementation gradientImageLayer

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super init]) {

        self.opaque = YES; // Best for performance, but you if you want the layer to have transparency, then remove.

        UIImage *i = [UIImage imageNamed:@"foo2.png"]; // Replace with your image

        self.frame = frame;

        self.contentsScale = [UIScreen mainScreen].nativeScale;
        self.contents = (__bridge id _Nullable)(i.CGImage);

        // Your code for the CAGradientLayer was indeed correct.
        CAGradientLayer *gradient = [CAGradientLayer layer];
        gradient.frame = frame;

        // Add whatever colors you want here.
        UIColor *colorOne = [UIColor colorWithRed:1 green:1 blue:0 alpha:0.1];
        UIColor *colorTwo = [UIColor colorWithRed:0 green:1 blue:1 alpha:0.2];

        gradient.colors = @[(id)colorOne.CGColor, (id)colorTwo.CGColor]; // Literals read far nicer than a clunky [NSArray arrayWith.... ]

        [self addSublayer:gradient];        
    }

    return self;
}

@end

这种方法的缺点是您无法应用不同的混合模式。我见过的在 CALayer 上应用混合模式的唯一解决方案是通过 Core Graphics,但你最好还是使用我原来的答案。

【讨论】:

  • 好的,我来实现这个!非常感谢您的回复!!
猜你喜欢
  • 2014-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-15
  • 1970-01-01
  • 2019-05-14
  • 2022-11-13
  • 1970-01-01
相关资源
最近更新 更多