聊聊iOS实现渐变色文本以及可能你不知道的细节

2022-01-09  本文已影响0人  JerryFans

前言

前段时间工作中,产品给了一个实现渐变色文本,并且是放到富文本里面的需求。插入到富文本这里先不说,无非就是生成这个渐变Label的一张Image插入到富文本。相信大家第一时间可能会去度娘寻求答案,并且马上就能搜索出答案。这里我们来聊聊几种方案的实现,以及产生的问题。

方案一

基于CAGradientLayer做一个mask,核心代码大概如下。

 override func layoutSubviews() {
        super.layoutSubviews()
        guard let config = self.config else { return }
        if self.gradientLayer != nil {
            return
        }
        let gradientLayer = CAGradientLayer()
        self.gradientLayer = gradientLayer
        gradientLayer.colors = [config.startColor.cgColor,config.endColor.cgColor]
        gradientLayer.startPoint = CGPoint(x: 0, y: 0)
        gradientLayer.endPoint = CGPoint(x: 1, y: 1)
        gradientLayer.frame = self.label.frame
        gradientLayer.mask = self.label.layer
        self.label.layer.frame = gradientLayer.bounds
        self.layer.insertSublayer(gradientLayer, at: 0)
    }

来看看实现效果:

image

看到这里大家是否意味已经大结局了?实际上我一开始确实用此方法直接提交给测试了,结果就翻车了。测试提交了一个如下的Bugs,如果显示了emoj表情,会显示上有bugs如下。

image

为什么会有此问题?原理上其实也很简单,CAGradientLayer的方式,实际上是在Label上面盖一层蒙版,如果用了emoj系统可不会给你考虑那么多,翻车倒是正常。所以接下来就要考虑另外一种方式了。

方案二

基于UIColor(patternImage: gradientImage),这个方法,直接给Label设置一张渐变的图片的颜色。(注意这个gradientImage要与你Label的Frame大小是要一致的,不然那个渐变效果不一定会和设计一致。)代码大概如下:

@objc convenience init(config: GradientPatternLabelConfig) {
        self.init(frame: .zero)
        self.config = config
        self.label.font = config.font
        self.label.text = config.text
        self.addSubview(self.label)
        self.label.sizeToFit()
        self.label.lineBreakMode = config.lineBreakMode
        let size = (config.maxWidth == 0 || self.label.jf.size.width < config.maxWidth) ? self.label.jf.size : CGSize(width: config.maxWidth, height: self.label.jf.size.height)
        if config.startColor == UIColor.clear {
            self.label.textColor = config.textColor
        }
        else if let gradientImage = ConvertGradientImage.gradientImage(with: config, size: size) {
            self.label.textColor = UIColor(patternImage: gradientImage)
        }
        else {
            self.label.textColor = config.startColor
        }
        let frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        self.label.frame = frame
        self.frame = frame
    }

生成gradientImage的代码:

+ (UIImage *_Nullable)gradientImageWithConfig:(GradientPatternLabelConfig *_Nonnull)config size:(CGSize)size {
    UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    //绘制渐变层
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradientRef = CGGradientCreateWithColors(colorSpaceRef,
                                                           (__bridge CFArrayRef)@[(id)config.startColor.CGColor,(id)config.endColor.CGColor],
                                                           NULL);
    CGPoint startPoint = CGPointZero;
    CGRect rect = CGRectMake(0, 0, size.width, size.height);
    CGPoint endPoint = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
    CGContextDrawLinearGradient(context, gradientRef, startPoint, endPoint,  kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
    //取到渐变图片
    UIImage *gradientImage = UIGraphicsGetImageFromCurrentImageContext();
    //释放资源
    CGColorSpaceRelease(colorSpaceRef);
    CGGradientRelease(gradientRef);
    UIGraphicsEndImageContext();
    return gradientImage;
}

接下来就是见证奇迹的时候:

image

Demo地址:

https://github.com/JerryFans/GradientLabelDemo

上一篇下一篇

猜你喜欢

热点阅读