如何在 iOS 中创建带有渐变的叠加颜色

How to create an overlay color with a gradient in iOS

背景信息:我有一个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 并将它的 backgroundColor 设置为 overlayColor,然后添加一个 CGGradientLayer 作为 UIViewsublayer =] 作为 subview 添加到 UIImageView 但是,我们不应该将 subviews 添加到 UIImageViews.

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

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

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

在此先感谢您的帮助!

编辑: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];

我只会使用 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))。

这样你就不用再弄乱图层了。如果您经常 re-drawing 使用不同的颜色,那么您可能想要采用使用图层的方法。

如果您正在寻找一种更动态的方法,那么我会继承 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,但是你最好使用我的原始答案。

我将 post 协作解决方案 question

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

Swift 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 {[=10=].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)