UIKit实现一个渐变文字的UILabel(核心思想及实现过程)

一. 引言

在日常开发中渐变背景我们并不陌生,而且官方几乎也给了方案使用CAGradientLayer特殊图层。但是渐变文字呢?这个设计样式不太常见,但是如果是做社交类应用的话,为了凸显用户的身份,会员,贵族等等信息,经常会出现渐变文字甚至是闪烁文字。

提到渐变文字很多人第一反应:

  • 能不能直接给 UILabel 设置渐变色?
  • drawText(in:) 自己画?
  • 或者用图片?

这些方案要么性能一般、要么不够优雅,也不利于组件化复用。

这篇文章我们来实现一个 真正工程可用的渐变文字 UILabel,方案稳定、性能友好,并且可以和常见的渐变按钮、渐变视图保持一致的设计风格。

二. 渐变文字的核心思路

先说结论:

文字本身并不负责显示颜色,颜色来自渐变 Layer;文字只是一个「遮罩(mask)」

整体结构就是:

ZMGradientLabel

└─ CAGradientLayer (真正有颜色)

└─ mask = CATextLayer(文字轮廓)

也就是说:

  • 渐变效果由 CAGradientLayer 提供
  • CATextLayer 只负责定义「文字长什么样」
  • 通过 mask,只显示文字形状区域内的渐变

这是一个 GPU 渲染方案,性能和系统动画完全同级。

三. 渐变文字的具体实现

3.1 定义 ZMGradientLabel

Swift 复制代码
import UIKit

public class ZMGradientLabel: UILabel {

    private let gradientLayer = CAGradientLayer()
    private let textMaskLayer = CATextLayer()

    // MARK: - Init
    public override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    public required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    private func commonInit() {
        // UILabel 本身不显示颜色
        textColor = .clear

        // 添加渐变 Layer
        layer.addSublayer(gradientLayer)
        gradientLayer.mask = textMaskLayer

        // CATextLayer 基础配置
        textMaskLayer.contentsScale = UIScreen.main.scale
        textMaskLayer.alignmentMode = .center
        textMaskLayer.truncationMode = .end
    }

    // MARK: - Layout
    public override func layoutSubviews() {
        super.layoutSubviews()
        gradientLayer.frame = bounds
        updateTextMask()
    }

    // MARK: - Text 更新
    private func updateTextMask() {
        textMaskLayer.frame = bounds
        textMaskLayer.string = text
        textMaskLayer.font = font
        textMaskLayer.fontSize = font.pointSize
    }

    public override var text: String? {
        didSet { updateTextMask() }
    }

    public override var font: UIFont! {
        didSet { updateTextMask() }
    }
}

到这里,我们已经完成了:

  • 渐变 Layer 的承载
  • 文字作为 Mask 的映射
  • Auto Layout 下的自适应更新

3.2 配置渐变颜色及方向

我们可以为 ZMGradientLabel 添加一个设置颜色及方向方法,来更新它的渐变颜色:

Swift 复制代码
extension ZMGradientLabel {

    public func setupGradient(
        colors: [UIColor],
        locations: [NSNumber]? = nil,
        direction: ZMGradientDirection = .leftToRight
    ) {
        var cgColors = colors.map { $0.cgColor }
        if cgColors.count == 1 {
            cgColors.append(cgColors[0])
        }

        gradientLayer.colors = cgColors
        gradientLayer.locations = locations

        switch direction {
        case .leftToRight:
            gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
            gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
        case .rightToLeft:
            gradientLayer.startPoint = CGPoint(x: 1, y: 0.5)
            gradientLayer.endPoint = CGPoint(x: 0, y: 0.5)
        case .topToBottom:
            gradientLayer.startPoint = CGPoint(x: 0.5, y: 0)
            gradientLayer.endPoint = CGPoint(x: 0.5, y: 1)
        case .bottomToTop:
            gradientLayer.startPoint = CGPoint(x: 0.5, y: 1)
            gradientLayer.endPoint = CGPoint(x: 0.5, y: 0)
        case .topLeftToBottomRight:
            gradientLayer.startPoint = CGPoint(x: 0, y: 0)
            gradientLayer.endPoint = CGPoint(x: 1, y: 1)
        case .bottomLeftToTopRight:
            gradientLayer.startPoint = CGPoint(x: 0, y: 1)
            gradientLayer.endPoint = CGPoint(x: 1, y: 0)
        }
    }
}

其中ZMGradientDirection 是我们自定义的枚举。

3.3 使用渐变文字

在使用时,就和普通的UILabel一样设置内容,设置字体,只是在需要渐变时,调用setupGradient方法来设置它的渐变颜色和方向。

Swift 复制代码
let label = ZMGradientLabel()
label.text = "渐变文字"
label.font = .boldSystemFont(ofSize: 20)

label.setupGradient(
    colors: [.systemPink, .systemOrange],
    direction: .leftToRight
)

支持特性:

  • Auto Layout / SnapKit
  • 动态修改 text / font
  • GPU 渲染,性能稳定

四. 结语

渐变文字看起来是一个「视觉需求」,但背后其实是一个非常典型的 Layer + Mask 应用场景。

相比 drawText 或图片方案,CAGradientLayer + CATextLayer

  • 性能更好(GPU 渲染)
  • 更易复用、易扩展
  • 更符合 UIKit 的底层设计哲学

当你已经有了渐变按钮、渐变背景时,把渐变文字也纳入同一套组件体系,会让整个 UI 在一致性和可维护性上都提升一个层级。

相关推荐
掘金安东尼11 小时前
让 JavaScript 更容易「善后」的新能力
前端·javascript·面试
掘金安东尼11 小时前
用 HTMX 为 React Data Grid 加速实时更新
前端·javascript·面试
灵感__idea13 小时前
Hello 算法:众里寻她千“百度”
前端·javascript·算法
yinuo14 小时前
轻松接入大语言模型API -04
前端
袋鼠云数栈UED团队15 小时前
基于 Lexical 实现变量输入编辑器
前端·javascript·架构
cipher15 小时前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
UrbanJazzerati15 小时前
非常友好的Vue 3 生命周期详解
前端·面试
AAA阿giao15 小时前
从零构建一个现代登录页:深入解析 Tailwind CSS + Vite + Lucide React 的完整技术栈
前端·css·react.js
兆子龙16 小时前
像 React Hook 一样「自动触发」:用 Git Hook 拦住忘删的测试代码与其它翻车现场
前端·架构
兆子龙17 小时前
用 Auto.js 实现挂机脚本:从找图点击到循环自动化
前端·架构