在iOS开发中,使用Core Graphics绘制自定义仪表盘速度表是一项非常实用的技能,尤其适用于需要展示实时速度、转速等连续变量的场景。相比使用图片或简单UIView拼接,Core Graphics能够提供像素级的绘制控制,让刻度、指针和背景完美融合。

一、Core Graphics绘制仪表盘基础
Core Graphics是iOS底层的二维绘图框架,常通过在UIView的子类中重写drawRect方法使用。要画仪表盘,第一步是理解坐标系:UIView的左上角为原点,向右为x正方向,向下为y正方向。通常我们会将圆心设在视图中心,通过CGContextTranslateCTM把坐标原点移到中心,这样后续以圆心为基准画圆弧和刻度会更直观。
绘制前需获取当前图形上下文,使用UIGraphicsGetCurrentContext函数。之后可以设置线条颜色、宽度与抗锯齿。比如用CGContextSetLineWidth设定刻度线粗细,用CGContextSetStrokeColorWithColor设定颜色。由于drawRect在每次视图刷新时都会调用,频繁创建对象会影响性能,因此尽量将不随数据变化的静态部分(如外圈、固定文字)缓存起来,只重绘速度和指针相关元素。
1.1 圆弧与刻度的数学关系
仪表盘一般不是完整圆,常见的是底部留缺口的270度弧。假设起始角度为135度(即右下偏下),结束角度为45度(顺时针绕到左下偏下),总共扫过270度。每根主刻度对应一个角度,可以通过起始角度加上(当前刻度值除以最大值乘以270度)来算出。注意Core Graphics的角度以弧度为单位,且0弧度在三点钟方向,顺时针为正,所以要用数学转换。
例如最大值220公里每小时,要把0到220映射到135度到405度(即45度加360度)之间。这样每个刻度位置都能用统一公式计算,避免硬编码。次要刻度可放在主刻度之间,长度更短、颜色更浅,增强可读性。
二、刻度渐变实现方法
单纯用单色画刻度虽然清晰,但缺乏视觉引导。真实汽车仪表常在低速度用绿色、高速度用红色来提醒驾驶者。Core Graphics的CGGradient正好适合做这种沿路径或区域的色彩过渡。我们可以创建从起点颜色到终点颜色的渐变,再配合裁剪区域把渐变限制在刻度环上。
具体做法是先用CGContextAddArc画一条代表刻度带的圆弧路径,接着用CGContextReplacePathWithStrokedPath把它转成可填充的描边区域,然后CGContextClip限制后续绘制。之后用CGGradientCreateWithColors生成绿到黄再到红的渐变,用CGContextDrawLinearGradient或径向渐变填充。这样整圈刻度就会自然变色,不需要一根根线单独设色。
2.1 渐变颜色节点设置
CGGradient的颜色停止点用CGFloat数组表示位置,0代表起点,1代表终点。若想绿色占前60百分比、黄色占中间20百分比、红色占后20百分比,就设置三个颜色并在0.0、0.6、0.8、1.0处放节点。实际中也可根据安全速度阈值动态调整,比如城市限速50以内全绿,超过180变红。
要注意iOS中UIColor的RGBA分量是0到1浮点数,而CGColorRef可直接从UIColor取。若用图片作为渐变来源会失去分辨率自由,Core Graphics矢量渐变在放大时依旧锐利,这也是其优势。
三、指针惯性动画原理
如果速度一变指针就瞬间转到新角度,用户会觉得像玩具。真实表针有质量和阻尼,加速和减速都有过程。在iOS里可以用CADisplayLink配合物理公式模拟,也可以借助UIDynamicBehavior,但仪表盘常用显式积分更可控。
惯性的核心是弹簧阻尼模型:设目标角度为to,当前角度为cur,速度为v。每帧计算加速度a = stiffness*(to-cur) - damping*v,然后v += a*dt,cur += v*dt。stiffness越大指针回得越快,damping越大摆动越少。调参后能得出类似机械表针的顺滑回弹,甚至轻微过冲。
3.1 与主线程刷新的结合
CADisplayLink能以屏幕刷新率调用selector,在方法里更新角度并调用setNeedsDisplay重绘。由于drawRect本身在CPU上跑,若刻度复杂可把静态层存到CALayer或图片,只让指针层独立动画,减少卡顿。
另外需处理速度突变,比如急刹车时目标角骤降,惯性模型会自动产生快速回转并可能小幅震荡,这恰恰符合物理直觉。若发现过冲太多,只需增大damping值即可。
四、速度到角度的映射与边界处理
速度映射指把业务数据(如车速)转换为指针角度。最常用线性映射:angle = startAngle + (speed/maxSpeed)*sweepAngle。但要防御输入越界,例如蓝牙传来的脏数据超过maxSpeed,应做clamp处理,否则指针会画出仪表盘。
也可做非线性映射,比如对数曲线让低速区分度更高,但这会让刻度不均,需在画刻度时也用同一函数,否则刻度和指针不匹配。对于标准车速表,线性最稳妥,用户认知成本低。
4.1 示例换算表
下面给出最大值220、起始135度、扫过270度时的部分映射示例,帮助理解:
| 速度(km/h) | 占比 | 角度(度) |
|---|---|---|
| 0 | 0 | 135 |
| 55 | 0.25 | 202.5 |
| 110 | 0.5 | 270 |
| 220 | 1 | 405 |
上表角度超过360时可对360取模显示,但计算时保留连续值更方便做动画差值。实际编码中建议把角度转弧度用宏或内联函数封装,减少重复代码。
五、综合实现建议
把仪表盘拆成三层:底层渐变刻度与背景在初始化时画一次;中间层数字标签随刻度固定;顶层指针用独立动画驱动。这样每次速度更新只重绘指针,既流畅又省电。若项目支持SwiftUI,也可用Canvas包装Core Graphics调用,但UIKit下自定义UIView依然最直接。
调试时建议先打印角度和速度对应关系,再慢慢调弹簧参数。真机测试注意深色模式下颜色对比,渐变节点可随traitCollection变化重新生成。掌握这些,就能做出媲美原生车机风格的iOS速度表。
Core_GraphicsiOS仪表盘指针惯性动画修改时间:2026-08-11 06:33:38