如何在 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
作为 UIView
的 sublayer
=] 作为 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
是输入图像,color1
和 color2
是您的渐变颜色,gradPointA
和 gradPointB
是您的线性渐变结束点(在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)
背景信息:我有一个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
作为 UIView
的 sublayer
=] 作为 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
是输入图像,color1
和 color2
是您的渐变颜色,gradPointA
和 gradPointB
是您的线性渐变结束点(在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)