iOS使用CoreGraphics绘制UIView无法显示交替颜色问题求助
嘿,我来帮你搞定这个CoreGraphics绘制交替颜色没显示的问题~先从核心原理捋清楚,再结合你的ProgressMenuView代码找可能踩的坑。
CoreGraphics(大伙常叫CG)是个状态机式的绘图框架,简单说就是:你得先给它设定好“当前状态”(比如填充色、线条粗细、要画的路径),然后喊它执行绘制指令,它就会用当前状态去渲染。而且状态是会累积的——除非你手动保存/恢复,不然后面的设置会直接覆盖前面的,这点很关键!
几个必走的绘制步骤:
- 第一步:创建/添加**路径(CGPath)**到当前绘图上下文,比如画矩形就调用
addRect(_:),画圆就用addArc(...) - 第二步:设置绘制状态,比如填充色
UIColor.setFill()、描边色setStroke() - 第三步:执行绘制,调用
fill()(填充)、stroke()(描边)或者fillStroke()(同时填充+描边)
要是这几步顺序错了(比如先画再设颜色),或者路径没加对,就会出现“啥也没画出来”的情况。
看你的代码,应该是想画一段段不同颜色的进度条对吧?结合你没写完的cumul...(猜是累积值,用来算每段的宽度),我列几个最可能的坑:
1. 数值没转换成视图内的比例
你的values数组是[50,100,150,180],但这些原始数值得先转换成视图宽度范围内的比例才行!比如总数值是50+100+150+180=480,那每一段的宽度应该是(单个数值 / 总数值) * 视图宽度,不然数值超过视图宽度的话,后面的段直接画出视图外,自然看不到。
2. 没给每段单独设置颜色+绘制
CG是状态机,要是你循环的时候只改路径不改颜色,或者颜色设置在绘制之后,要么所有段都是最后一个颜色,要么干脆没颜色。正确的循环逻辑应该是这样:
override func draw(_ rect: CGRect) { // 先确保能拿到绘图上下文,拿不到直接返回 guard let ctx = UIGraphicsGetCurrentContext() else { return } // 计算总数值,用来算每段的比例 let totalValue = values.reduce(0, +) // 记录当前绘制的起始X坐标,一开始从0开始 var currentX: CGFloat = 0 // 遍历每个数值和对应的颜色 for (index, value) in values.enumerated() { // 计算当前段的宽度 let segmentWidth = (value / totalValue) * rect.width // 创建当前段的矩形路径 let segmentRect = CGRect(x: currentX, y: 0, width: segmentWidth, height: rect.height) ctx.addRect(segmentRect) // 设置当前段的填充色 colors[index].setFill() // 填充当前路径,注意fillPath()会清空当前路径,避免和下一段混在一起 ctx.fillPath() // 更新起始X坐标,准备画下一段 currentX += segmentWidth } }
这里重点是:每加一段路径,就立刻设置对应颜色,然后填充,这样每段都是独立的,交替颜色自然就出来了。
3. 视图属性没设置对
要是你的视图默认背景色和绘制颜色一样,或者opaque属性没设对,也会看起来没显示。可以在初始化方法里加几句:
override init(frame: CGRect) { super.init(frame: frame) self.backgroundColor = .clear // 避免和绘制颜色冲突,也可以设你要的背景色 self.isOpaque = false // 要是有透明区域的话,必须设成false } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.backgroundColor = .clear self.isOpaque = false }
4. 没处理上下文为空的情况
一定要加guard let ctx = UIGraphicsGetCurrentContext() else { return },要是上下文拿不到,后面的代码全白跑了,自然啥也画不出来。
- 坐标系:CG默认原点在左下角,但在
draw(_ rect:)方法里,系统已经帮你把上下文坐标系转换成UIKit的左上角了,所以直接按UIKit的坐标逻辑画就行,不用自己转。 - 状态保存:要是你需要临时改状态(比如换线条宽度),记得用
ctx.saveGState()保存当前状态,改完之后用ctx.restoreGState()恢复,不然会影响后面的绘制。 - 性能优化:要是你的视图需要频繁刷新,最好把路径缓存起来,别每次
draw都重新计算路径,能省不少性能。
内容的提问来源于stack exchange,提问作者user5336403

