聊聊iOS實現漸變色文本以及可能你不知道的細節

語言: CN / TW / HK

前言

前段時間工作中,產品給了一個實現漸變色文本,並且是放到富文本里面的需求。插入到富文本這裏先不説,無非就是生成這個漸變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)

}

```

來看看實現效果:

看到這裏大家是否意味已經大結局了?實際上我一開始確實用此方法直接提交給測試了,結果就翻車了。測試提交了一個如下的Bugs,如果顯示了emoj表情,會顯示上有bugs如下。

為什麼會有此問題?原理上其實也很簡單,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;

}

```

接下來就是見證奇蹟的時候:

Demo地址:

https://github.com/JerryFans/GradientLabelDemo