You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

drawRect绘制图表:文本“Today”被渐变遮罩截断的问题求助

解决drawRect中文本被渐变遮罩截断的问题

这大概率是因为遮罩的裁剪范围包含了文本区域,或者绘制顺序搞反了——Core Graphics里的遮罩只会对设置之后的绘制内容生效,或者如果路径没排除文本区域,就会把文本一起裁掉。我给你两个实用的解决思路:

思路1:调整绘制顺序(最简单直接)

先把"Today"文本画出来,再应用渐变遮罩绘制图表区域,这样文本完全不受遮罩影响:

override func draw(_ rect: CGRect) {
    super.draw(rect)
    guard let context = UIGraphicsGetCurrentContext() else { return }
    
    // 第一步:先绘制文本,这一步不受后续遮罩影响
    let todayText = "Today"
    let textAttributes: [NSAttributedString.Key: Any] = [
        .font: UIFont.systemFont(ofSize: 14),
        .foregroundColor: UIColor.black
    ]
    // 替换成你实际的文本位置和尺寸
    let textRect = CGRect(x: 20, y: 20, width: 100, height: 20)
    todayText.draw(in: textRect, withAttributes: textAttributes)
    
    // 第二步:设置渐变遮罩并绘制图表
    let gradStartColor = UIColor(white: 1, alpha: 0)
    let gradEndColor = UIColor(white: 0, alpha: 1)
    let colors = [gradStartColor.cgColor, gradEndColor.cgColor] as CFArray
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let colorLocations: [CGFloat] = [0.0, 1.0]
    
    guard let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: colorLocations) else { return }
    
    // 替换成你实际的图表路径
    let chartPath = UIBezierPath(rect: bounds)
    context.addPath(chartPath.cgPath)
    context.clip() // 裁剪仅对后续的渐变绘制生效,不影响已画好的文本
    
    // 绘制渐变
    let startPoint = CGPoint(x: bounds.midX, y: bounds.minY)
    let endPoint = CGPoint(x: bounds.midX, y: bounds.maxY)
    context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: [])
}

思路2:修改遮罩路径,排除文本区域

如果必须先画渐变再画文本(比如需要文本和渐变有混合效果),可以用奇偶裁剪把文本区域从遮罩里排除:

override func draw(_ rect: CGRect) {
    super.draw(rect)
    guard let context = UIGraphicsGetCurrentContext() else { return }
    
    let gradStartColor = UIColor(white: 1, alpha: 0)
    let gradEndColor = UIColor(white: 0, alpha: 1)
    let colors = [gradStartColor.cgColor, gradEndColor.cgColor] as CFArray
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let colorLocations: [CGFloat] = [0.0, 1.0]
    
    guard let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: colorLocations) else { return }
    
    // 1. 添加图表的主路径
    let chartPath = UIBezierPath(rect: bounds)
    context.addPath(chartPath.cgPath)
    
    // 2. 添加文本所在区域的路径(要排除的区域)
    let textRect = CGRect(x: 20, y: 20, width: 100, height: 20) // 替换成实际文本区域
    let textPath = UIBezierPath(rect: textRect)
    context.addPath(textPath.cgPath)
    
    // 3. 用奇偶裁剪:路径重叠奇数次的区域被裁剪,偶数次的保留(文本区域重叠两次,不会被裁)
    context.eoClip()
    
    // 4. 绘制渐变
    let startPoint = CGPoint(x: bounds.midX, y: bounds.minY)
    let endPoint = CGPoint(x: bounds.midX, y: bounds.maxY)
    context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: [])
    
    // 5. 绘制文本(此时文本区域无渐变遮罩)
    let todayText = "Today"
    let textAttributes: [NSAttributedString.Key: Any] = [
        .font: UIFont.systemFont(ofSize: 14),
        .foregroundColor: UIColor.black
    ]
    todayText.draw(in: textRect, withAttributes: textAttributes)
}

核心知识点

  • CGContextClip() 用的是非零缠绕数规则,会裁剪所有被路径包含的区域
  • CGContextEOClip() 用的是奇偶规则,路径重叠次数为偶数的区域会被保留
  • Core Graphics的绘制顺序极其关键:先画的内容不会被后续的遮罩、裁剪操作影响

内容的提问来源于stack exchange,提问作者Kex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:01:24