preface

Some time ago in work, the product gave a requirement to implement gradient text and put it inside rich text. I’m going to insert it into rich text and I’m not going to talk about it here, but it’s just an Image that generates this gradient Label and I’m going to insert it into rich text. I believe you may go to Baidu for the first time to seek the answer, and immediately can search the answer. Here we talk about the implementation of several solutions, and the problems that arise.

Plan a

Make a mask based on CAGradientLayer, the core code is roughly as follows.

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) }Copy the code

Take a look at the implementation:

Does seeing everyone here mean it’s over? In fact, I did submit it directly to the test in the first place, and it crashed. The test submitted the following Bugs, which if the emoj expression was displayed would show Bugs as follows.

Why is this a problem? In fact, the principle is very simple, CAGradientLayer is actually a mask on the Label, if the EMOJ system will not give you so much consideration, it is normal to roll over. So the next thing to think about is another way.

Scheme 2

Based on UIColor(patternImage: gradientImage), this method sets a gradientImage color directly to the Label. (Note that gradientImage needs to be consistent with the Frame size of your Label, otherwise the gradient will not necessarily be consistent with the design.) The code looks like this:

@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 }Copy the code

Generate gradientImage code:

+ (UIImage *_Nullable)gradientImageWithConfig:(GradientPatternLabelConfig *_Nonnull)config size:(CGSize)size { UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale); CGContextRef context = UIGraphicsGetCurrentContext(); / / draw the gradient layer 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); / / to the gradient image UIImage * gradientImage = UIGraphicsGetImageFromCurrentImageContext (); CGColorSpaceRelease(colorSpaceRef); CGGradientRelease(gradientRef); UIGraphicsEndImageContext(); return gradientImage; }Copy the code

Then came the miracle:

The Demo address:

Github.com/JerryFans/G…