嘿,我希望能够在 UILabel 中的文本上进行渐变填充,我知道 CGGradient,但我不知道如何在 UILabel 的文本上使用它
我在谷歌上找到了这个,但我无法让它工作
嘿,我希望能够在 UILabel 中的文本上进行渐变填充,我知道 CGGradient,但我不知道如何在 UILabel 的文本上使用它
我在谷歌上找到了这个,但我无法让它工作
我一直在寻找解决方案,而 DotSlashSlash 的答案隐藏在其中一条评论中!
为了完整起见,答案和最简单的解决方案是:
UIImage *myGradient = [UIImage imageNamed:@"textGradient.png"];
myLabel.textColor = [UIColor colorWithPatternImage:myGradient];
(跳到底部查看完整的课程源代码)
Brad Larson 和 Bach 的真正有用的答案。第二个对我有用,但它需要预先存在图像。我想要更动态的东西,所以我将两种解决方案合二为一:
结果有效,在下面的屏幕截图中,您也可以看到一些希腊字符也渲染得很好。(我还在渐变顶部添加了描边和阴影)
这是我的标签的自定义 init 方法以及在 UIImage 上呈现渐变的方法(我从博客文章中获得的该功能的部分代码,我现在找不到引用它):
- (id)initWithFrame:(CGRect)frame text:(NSString *)aText {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
self.text = aText;
self.textColor = [UIColor colorWithPatternImage:[self gradientImage]];
}
return self;
}
- (UIImage *)gradientImage
{
CGSize textSize = [self.text sizeWithFont:self.font];
CGFloat width = textSize.width; // max 1024 due to Core Graphics limitations
CGFloat height = textSize.height; // max 1024 due to Core Graphics limitations
// create a new bitmap image context
UIGraphicsBeginImageContext(CGSizeMake(width, height));
// get context
CGContextRef context = UIGraphicsGetCurrentContext();
// push context to make it current (need to do this manually because we are not drawing in a UIView)
UIGraphicsPushContext(context);
//draw gradient
CGGradientRef glossGradient;
CGColorSpaceRef rgbColorspace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 0.0, 1.0, 1.0, 1.0, // Start color
1.0, 1.0, 0.0, 1.0 }; // End color
rgbColorspace = CGColorSpaceCreateDeviceRGB();
glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
CGPoint topCenter = CGPointMake(0, 0);
CGPoint bottomCenter = CGPointMake(0, textSize.height);
CGContextDrawLinearGradient(context, glossGradient, topCenter, bottomCenter, 0);
CGGradientRelease(glossGradient);
CGColorSpaceRelease(rgbColorspace);
// pop context
UIGraphicsPopContext();
// get a UIImage from the image context
UIImage *gradientImage = UIGraphicsGetImageFromCurrentImageContext();
// clean up drawing environment
UIGraphicsEndImageContext();
return gradientImage;
}
我将尝试完成该 UILabel 子类并发布它。
编辑:
课程完成了,它在我的 GitHub 存储库上。在这里阅读它!
斯威夫特 4.1
class GradientLabel: UILabel {
var gradientColors: [CGColor] = []
override func drawText(in rect: CGRect) {
if let gradientColor = drawGradientColor(in: rect, colors: gradientColors) {
self.textColor = gradientColor
}
super.drawText(in: rect)
}
private func drawGradientColor(in rect: CGRect, colors: [CGColor]) -> UIColor? {
let currentContext = UIGraphicsGetCurrentContext()
currentContext?.saveGState()
defer { currentContext?.restoreGState() }
let size = rect.size
UIGraphicsBeginImageContextWithOptions(size, false, 0)
guard let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(),
colors: colors as CFArray,
locations: nil) else { return nil }
let context = UIGraphicsGetCurrentContext()
context?.drawLinearGradient(gradient,
start: CGPoint.zero,
end: CGPoint(x: size.width, y: 0),
options: [])
let gradientImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let image = gradientImage else { return nil }
return UIColor(patternImage: image)
}
}
用法:
label.gradientColors = [UIColor.blue.cgColor, UIColor.red.cgColor]
斯威夫特 3+
该解决方案基于@Dimitris 的回答。它是UILabel
该类的扩展,它将根据您传递的标签文本创建渐变startColor
和endColor
。UILabel
扩展如下:
extension UILabel {
func applyGradientWith(startColor: UIColor, endColor: UIColor) -> Bool {
var startColorRed:CGFloat = 0
var startColorGreen:CGFloat = 0
var startColorBlue:CGFloat = 0
var startAlpha:CGFloat = 0
if !startColor.getRed(&startColorRed, green: &startColorGreen, blue: &startColorBlue, alpha: &startAlpha) {
return false
}
var endColorRed:CGFloat = 0
var endColorGreen:CGFloat = 0
var endColorBlue:CGFloat = 0
var endAlpha:CGFloat = 0
if !endColor.getRed(&endColorRed, green: &endColorGreen, blue: &endColorBlue, alpha: &endAlpha) {
return false
}
let gradientText = self.text ?? ""
let name:String = NSFontAttributeName
let textSize: CGSize = gradientText.size(attributes: [name:self.font])
let width:CGFloat = textSize.width
let height:CGFloat = textSize.height
UIGraphicsBeginImageContext(CGSize(width: width, height: height))
guard let context = UIGraphicsGetCurrentContext() else {
UIGraphicsEndImageContext()
return false
}
UIGraphicsPushContext(context)
let glossGradient:CGGradient?
let rgbColorspace:CGColorSpace?
let num_locations:size_t = 2
let locations:[CGFloat] = [ 0.0, 1.0 ]
let components:[CGFloat] = [startColorRed, startColorGreen, startColorBlue, startAlpha, endColorRed, endColorGreen, endColorBlue, endAlpha]
rgbColorspace = CGColorSpaceCreateDeviceRGB()
glossGradient = CGGradient(colorSpace: rgbColorspace!, colorComponents: components, locations: locations, count: num_locations)
let topCenter = CGPoint.zero
let bottomCenter = CGPoint(x: 0, y: textSize.height)
context.drawLinearGradient(glossGradient!, start: topCenter, end: bottomCenter, options: CGGradientDrawingOptions.drawsBeforeStartLocation)
UIGraphicsPopContext()
guard let gradientImage = UIGraphicsGetImageFromCurrentImageContext() else {
UIGraphicsEndImageContext()
return false
}
UIGraphicsEndImageContext()
self.textColor = UIColor(patternImage: gradientImage)
return true
}
}
和用法:
let text = "YAAASSSSS!"
label.text = text
if label.applyGradientWith(startColor: .red, endColor: .blue) {
print("Gradient applied!")
}
else {
print("Could not apply gradient")
label.textColor = .black
}
斯威夫特 2
class func getGradientForText(text: NSString) -> UIImage {
let font:UIFont = UIFont(name: "YourFontName", size: 50.0)!
let name:String = NSFontAttributeName
let textSize: CGSize = text.sizeWithAttributes([name:font])
let width:CGFloat = textSize.width // max 1024 due to Core Graphics limitations
let height:CGFloat = textSize.height // max 1024 due to Core Graphics limitations
//create a new bitmap image context
UIGraphicsBeginImageContext(CGSizeMake(width, height))
// get context
let context = UIGraphicsGetCurrentContext()
// push context to make it current (need to do this manually because we are not drawing in a UIView)
UIGraphicsPushContext(context!)
//draw gradient
let glossGradient:CGGradientRef?
let rgbColorspace:CGColorSpaceRef?
let num_locations:size_t = 2
let locations:[CGFloat] = [ 0.0, 1.0 ]
let components:[CGFloat] = [(202 / 255.0), (197 / 255.0), (52 / 255.0), 1.0, // Start color
(253 / 255.0), (248 / 255.0), (101 / 255.0), 1.0] // End color
rgbColorspace = CGColorSpaceCreateDeviceRGB();
glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
let topCenter = CGPointMake(0, 0);
let bottomCenter = CGPointMake(0, textSize.height);
CGContextDrawLinearGradient(context, glossGradient, topCenter, bottomCenter, CGGradientDrawingOptions.DrawsBeforeStartLocation);
// pop context
UIGraphicsPopContext();
// get a UIImage from the image context
let gradientImage = UIGraphicsGetImageFromCurrentImageContext();
// clean up drawing environment
UIGraphicsEndImageContext();
return gradientImage;
}
@Dimitris 的道具
您提供的示例依赖于您在 iPhone 上无法访问的私有文本绘图功能。 作者在后续文章中提供了如何使用公共 API 执行此操作的示例。他后面的示例使用渐变图像作为文本颜色。(不幸的是,他的博客似乎已被删除,但请参阅巴赫的回答,了解他使用的方法。)
如果您仍想在代码中为文本颜色绘制渐变,可以通过子类化 UILabel 并覆盖 -drawRect: 来在其中包含如下代码:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0f, self.bounds.size.height);
CGContextScaleCTM(context, 1.0f, -1.0f);
CGContextSelectFont(context, "Helvetica", 20.0f, kCGEncodingMacRoman);
CGContextSetTextDrawingMode(context, kCGTextClip);
CGContextSetTextPosition(context, 0.0f, round(20.0f / 4.0f));
CGContextShowText(context, [self.text UTF8String], strlen([self.text UTF8String]));
CGContextClip(context);
CGGradientRef gradient;
CGColorSpaceRef rgbColorspace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 1.0, 1.0, 1.0, 1.0, // Start color
1.0, 1.0, 1.0, 0.1 }; // End color
rgbColorspace = CGColorSpaceCreateDeviceRGB();
gradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
CGRect currentBounds = self.bounds;
CGPoint topCenter = CGPointMake(CGRectGetMidX(currentBounds), 0.0f);
CGPoint midCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMidY(currentBounds));
CGContextDrawLinearGradient(context, gradient, topCenter, midCenter, 0);
CGGradientRelease(gradient);
CGColorSpaceRelease(rgbColorspace);
CGContextRestoreGState(context);
这种方法的一个缺点是我使用的 Core Graphics 函数不能正确处理 Unicode 文本。
代码的作用是垂直翻转绘图上下文(iPhone 反转 Y 轴上的正常 Quartz 坐标系),设置文本绘制模式以将绘制的文本与剪切路径相交,剪切要绘制到文本的区域,然后绘制渐变。渐变只会填充文本,不会填充背景。
我尝试为此使用 NSString 的 -drawAtPoint: 方法,该方法确实支持 Unicode,但是当我将文本模式切换到 kCGTextClip 时,所有字符都相互重叠。
这是我在 Swift 3 中所做的
override func viewDidLoad() {
super.viewDidLoad()
timerLabel.textColor = UIColor(patternImage: gradientImage(size: timerLabel.frame.size, color1: CIColor(color: UIColor.green), color2: CIColor(color: UIColor.red), direction: .Left))
}
func gradientImage(size: CGSize, color1: CIColor, color2: CIColor, direction: GradientDirection = .Up) -> UIImage {
let context = CIContext(options: nil)
let filter = CIFilter(name: "CILinearGradient")
var startVector: CIVector
var endVector: CIVector
filter!.setDefaults()
switch direction {
case .Up:
startVector = CIVector(x: size.width * 0.5, y: 0)
endVector = CIVector(x: size.width * 0.5, y: size.height)
case .Left:
startVector = CIVector(x: size.width, y: size.height * 0.5)
endVector = CIVector(x: 0, y: size.height * 0.5)
case .UpLeft:
startVector = CIVector(x: size.width, y: 0)
endVector = CIVector(x: 0, y: size.height)
case .UpRight:
startVector = CIVector(x: 0, y: 0)
endVector = CIVector(x: size.width, y: size.height)
}
filter!.setValue(startVector, forKey: "inputPoint0")
filter!.setValue(endVector, forKey: "inputPoint1")
filter!.setValue(color1, forKey: "inputColor0")
filter!.setValue(color2, forKey: "inputColor1")
let image = UIImage(cgImage: context.createCGImage(filter!.outputImage!, from: CGRect(x: 0, y: 0, width: size.width, height: size.height))!)
return image
}
有一个非常简单的解决方案!这是向 UILabel 文本添加渐变颜色的方法。
我们只需两步即可实现:
1.创建渐变图像
extension UIImage {
static func gradientImageWithBounds(bounds: CGRect, colors: [CGColor]) -> UIImage {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = colors
UIGraphicsBeginImageContext(gradientLayer.bounds.size)
gradientLayer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
如下使用它:
let gradientImage = UIImage.gradientImageWithBounds(bounds: myLabel.bounds, colors: [firstColor.cgColor, secondColor.cgColor])
⠀
2.将渐变图像作为文本颜色应用到UILabel
myLabel.textColor = UIColor.init(patternImage: gradientImage)
⠀
笔记:
如果您希望渐变是水平的,只需将这两行添加到 gradientLayer 实例:
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
⠀
笔记2:
UIImage 扩展功能也适用于其他 UIView;不仅仅是UILabel!因此,无论您使用哪个 UIView 应用渐变色,都可以随意使用此方法。
yourLabel.textColor = UIColor(patternImage: UIImage(named: "ur gradient image name ")!)
最简单的 Swift 3 解决方案
将图像添加到您的项目资产或以编程方式创建一个,然后执行以下操作:
let image = UIImage(named: "myGradient.png")!
label.textColor = UIColor.init(patternImage: image)
虽然我们Text
在 SwiftUI 中使用而不是 UILabel,但如果您考虑如何在 a 上应用渐变Text
,则应该将其应用为 a mask
。但是由于渐变是可拉伸的,你可以像这样做一个简单的扩展:
extension View {
func selfSizeMask<T: View>(_ mask: T) -> some View {
ZStack {
self.opacity(0)
mask.mask(self)
}.fixedSize()
}
}
然后您可以将任何渐变或其他类型的视图分配为自大小蒙版,例如:
Text("Gradient is on FIRE !!!")
.selfSizeMask(
LinearGradient(
gradient: Gradient(colors: [.red, .yellow]),
startPoint: .bottom,
endPoint: .top)
)
您可以将 UILable 子类化并自己执行 draw 方法。那可能是更困难的方法,可能有更简单的方法。