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
相关产品推荐
相关产品推荐

