Figma ios vector vs code
Skill mythkiven/figma-ios-codegen/.cursor/skills/figma-ios-vector-vs-code
Figma to iOS UIKit codegen: deterministic data package + Agent skills (Cursor/Claude) for baseline Swift UI.
npx -y skills add mythkiven/figma-ios-codegen --skill figma-ios-vector-vs-codeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 25 days oldThe repository was created 25 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 8 stars8 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
规定哪些 Figma 矢量图形应该下载为图片 Assets,哪些应该用 iOS 代码绘制。 避免不必要的图片下载,减少包体积。 Use when generating code from Figma designs with vector graphics, shapes, or masks.
SKILL.md
28.3 KB, as published. Nobody here has run it
Figma 矢量图形处理策略
⛔ 红线规则(最高优先级,覆盖所有下文)
含「语义子节点」的容器节点,即使被 figma-ios-preload-data 标记为 is_export_asset=true,也禁止整组用切图替代,必须按子节点逐一生成代码。
判定:什么是「语义子节点」
只要节点的子树(含跨层)中任意一个节点满足以下任一条件,该节点就含语义子节点:
| 条件 | 说明 | 例 |
|---|---|---|
type == "TEXT" | 含可读文本 | "STEP.1"、"找人试音"、"绝地求生" |
type == "INSTANCE" | 含组件实例(按钮/标签/图标 …) | 品类卡片、段位标签、性别 pill |
_role.is_iconfont == true | 含 iconfont | next/query/check 图标 |
_role.is_iconfont_library | 含 iconfont 集合容器 | iconfont_24 |
_role.is_list_container | 含横向 / 纵向列表容器 | 可滚动、Tag 集合 |
设计师在 Figma 上勾导出(export)经常是出于"备份给开发"目的,不代表这个组应该当成一张静态图。把"步骤条整组""5 个品类整组"做成切图,会直接导致:① 文字写死、改不了;② 状态切换不可能(选中/未选中);③ 国际化失败;④ 视觉看似 OK 实则功能完全缺失。这是历史上多次出现的高发陷阱,必须强制规避。
决策伪代码(先于"代码绘制 vs 下载图片"决策)
def can_use_export_asset(node):
if not (node.get("export") or {}).get("is_export_asset"):
return False
# 判定是否含语义子节点
for child in walk_subtree(node):
if child["type"] in ("TEXT", "INSTANCE"):
return False
role = child.get("_role") or {}
if role.get("is_iconfont") or role.get("is_iconfont_library"):
return False
if role.get("is_list_container"):
return False
return True # 子树纯几何/矢量,整组切图安全
允许整组切图的典型场景(白名单)
- 纯位图节点(
fills[].type == "IMAGE") BOOLEAN_OPERATION美术字 / Logo(无 TEXT 子节点)- 纯矢量装饰(背景插画、底纹)
- 单个 imageset 的图标(无子节点)
红线违反后的处置
如果你已经写出 UIImageView(image: UIImage(named: "img_xxx")) 来代替一个含 TEXT/INSTANCE 的节点,回退该实现,改为:
- 把这个 GROUP/FRAME 当作
UIView容器; - 它的子节点逐一生成对应的
UILabel/UIButton/UIView/ iconfontUILabel; - 不需要切图(因为切图是为了避免重写美术字之类的 BOOLEAN_OPERATION,与文字/图标无关)。
例外:节点确实是「设计师做的截屏占位图」(如设计稿里贴了一张"竞品截图"用作后续修改参考),且团队明确要先按图占位的 → 在代码顶部加
// TODO(阶段2): 拆解 <node_id> <name> 为子节点实现,并在 README 列出,不允许直接当阶段 2 交付。
本 Skill 的职责
规定在 Figma → iOS 代码生成时:
- 哪些矢量图形必须下载为图片 Assets
- 哪些矢量图形应该用代码绘制(
UIView+CALayer+UIBezierPath) - 如何判断和决策
决策流程图
Figma 矢量节点
↓
┌──────────────────────────────────────┐
│ 1. 是否为简单几何形状? │
│ - 矩形、圆角矩形、圆形、椭圆 │
│ - 纯色填充或简单渐变 │
│ - 无复杂路径或布尔运算 │
└──────────────────────────────────────┘
↓ 是 ↓ 否
用代码绘制 继续判断
↓
┌──────────────────────────────────────┐
│ 2. 是否为复杂矢量图形? │
│ - 贝塞尔曲线路径(≥3个) │
│ - 布尔运算(UNION/SUBTRACT/EXCLUDE)│
│ - 美术字体、Logo、特殊图标 │
└──────────────────────────────────────┘
↓ 是 ↓ 否
下载为图片 用代码绘制
规则 1:用代码绘制(推荐优先)
适用场景
以下 Figma 节点类型必须用代码绘制,禁止下载为图片:
| Figma 类型 | 识别特征 | iOS 实现 | 示例 |
|---|---|---|---|
| 矩形 | RECTANGLE,无圆角或圆角简单 | UIView + backgroundColor | 纯色背景 |
| 圆角矩形 | RECTANGLE,cornerRadius ≤ 50 | UIView + layer.cornerRadius | 按钮背景 |
| 圆形 | ELLIPSE,宽高相等 | UIView + layer.cornerRadius = width/2 | 头像容器 |
| 渐变背景 | fills[0].type == "GRADIENT_LINEAR" | CAGradientLayer | 渐变按钮 |
| 纯色图形 | 单一 SOLID 填充,简单形状 | UIView + backgroundColor | 分隔线 |
| 边框 | strokes 数组 | layer.borderWidth + borderColor | 输入框边框 |
⚠️ 强制规则:用数据包字段判断"代码绘制 vs 切图"
figma-ios-preload-data 已经在阶段 1 把每个节点的 type / fills / strokes / corner_radius / vector_geometry / effects / export.is_export_asset 全部规范化进 design.json,并:
- 设计师有 export →
node.export.is_export_asset = true且对应 imageset 已在assets/ios/→ 直接用切图 - 设计师无 export → 按下面的判定标准决定 代码绘制 或 生成 mock 图片
1. design.json[node].export.is_export_asset == true
→ 用 assets/ios/manifest.json 中的 asset_name → UIImage(named:)
2. 否则按 should_draw_with_code(node) 判断(见下面"判断标准")
→ true: 用代码绘制(UIView/CALayer/CAGradientLayer)
→ false: 加 TODO 注释 + mock 占位
阶段 2 不再调 MCP / Figma REST API;判断只看
design.json[node]字段。
判断标准(代码绘制)
满足以下所有条件时,用代码绘制:
✅ 形状简单(矩形/圆/椭圆)
✅ 填充简单(纯色/线性渐变/径向渐变)
✅ 无复杂路径(子路径 ≤ 1 个)
✅ 无布尔运算(UNION/SUBTRACT/INTERSECT/EXCLUDE)
✅ 圆角简单(`cornerRadius` 统一或仅部分圆角)
✅ 尺寸固定或可缩放(不依赖精确像素)
✅ 无复杂效果(effects 数组为空或 visible=false)
⚠️ 特殊检查:VECTOR 节点的复杂圆角
问题背景:
Figma 的 VECTOR 节点可能包含复杂的圆角路径(如步进器的单边圆角),但 corner_radius 会显示为 0。
触发条件(数据包字段):
node.type == "VECTOR"(或含路径的布尔/星形等)node.corner_radius缺失或为 0,且无明显corner_radii- 但
node.vector_geometry.fill_paths或stroke_paths非空(阶段 1 从 RESTfillGeometry/strokeGeometry写入)
判断:读 SVG path 字符串:
design.json[node].vector_geometry.fill_paths[].data
design.json[node].vector_geometry.stroke_paths[].data
(旧文档里的 vector_paths 已废弃,数据包不再写该字段。)
判断圆角类型:
<!-- 示例:步进器左侧按钮背景(单边圆角)-->
<path d="M0 16C0 7.16344 7.16344 0 16 0H32V32H16C7.16344 32 0 24.8366 0 16Z"
fill="white" fill-opacity="0.1"/>
分析 SVG path:
| Path 特征 | 识别方法 | 圆角类型 |
|---|---|---|
C 或 A 命令 | 有圆弧(贝塞尔曲线或弧线) | 有圆角 |
| 圆弧在四周 | 4 个 C/A 命令分布均匀 | 全圆角 |
| 圆弧在部分边 | 1-2 个 C/A 命令 | 单边或双边圆角 |
无 C/A 命令 | 只有 L/H/V 命令 | 无圆角(矩形) |
决策表:
| SVG path 特征 | 圆角类型 | iOS 实现 | 代码绘制? |
|---|---|---|---|
无 C/A 命令 | 无圆角 | UIView + backgroundColor | ✅ 是 |
| 4 个相同的圆弧 | 全圆角 | layer.cornerRadius = X | ✅ 是 |
| 单边或双边圆弧 | 部分圆角 | layer.cornerRadius + layer.maskedCorners | ✅ 是 |
| 复杂路径 | 不规则圆角 | 下载图片或 UIBezierPath | ❌ 否 |
实现示例(单边圆角):
// 步进器减少按钮(左边圆角,右边直角)
// design.json: type=VECTOR, corner_radius=0
// vector_geometry.fill_paths[0].data: "M0 16C0 7.16344 7.16344 0 16 0H32V32H16C7.16344 32 0 24.8366 0 16Z"
// 识别:左边有圆弧,右边是直线 → 单边圆角
private lazy var decreaseBackgroundView: UIView = {
let v = UIView()
v.backgroundColor = UIColor(white: 1.0, alpha: 0.1)
// 左边圆角,右边直角
v.layer.cornerRadius = 16 // 高度 32 / 2 = 16
v.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner] // 左上和左下
return v
}()
// 步进器增加按钮(右边圆角,左边直角)
private lazy var increaseBackgroundView: UIView = {
let v = UIView()
v.backgroundColor = UIColor(white: 1.0, alpha: 0.1)
// 右边圆角,左边直角
v.layer.cornerRadius = 16
v.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner] // 右上和右下
return v
}()
iOS 12.0+ 兼容性:
- ✅
layer.maskedCorners可用于 iOS 11.0+ - ✅ 项目最低部署 iOS 12.0,可以安全使用
常见单边圆角场景:
// 左边圆角
v.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner]
// 右边圆角
v.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner]
// 上边圆角
v.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
// 下边圆角
v.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
// 对角圆角(左上+右下)
v.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMaxYCorner]
检查清单(VECTOR 节点):
□ 已读取 design.json[node].corner_radius
□ 如果 corner_radius = 0 且 type = VECTOR
→ 已读取 design.json[node].vector_geometry.fill_paths / stroke_paths
→ 已解析 SVG path(`.data`)
→ 已判断圆角类型
□ 如果是单边圆角 → 使用 layer.maskedCorners
□ 如果是复杂路径 → 下载图片或 UIBezierPath
判断标准(代码绘制)
满足以下所有条件时,用代码绘制:
✅ 形状简单(矩形/圆/椭圆)
✅ 填充简单(纯色/线性渐变/径向渐变)
✅ 无复杂路径(子路径 ≤ 1 个)
✅ 无布尔运算(UNION/SUBTRACT/INTERSECT/EXCLUDE)
✅ 圆角简单(`cornerRadius` 统一或仅部分圆角)
✅ 尺寸固定或可缩放(不依赖精确像素)
✅ 无复杂效果(effects 数组为空或 visible=false)
示例 1:Mask(黄色圆角矩形)
Figma 数据:
{
"id": "1:306",
"name": "Mask",
"type": "VECTOR",
"fills": [{
"type": "SOLID",
"color": {"r": 1.0, "g": 0.9647, "b": 0.0235} // #FFF606
}],
"cornerRadius": 25.0,
"cornerSmoothing": 0.6,
"absoluteBoundingBox": {"width": 343.0, "height": 48.0}
}
判断:
- ✅ 形状简单:矩形
- ✅ 填充简单:纯色 #FFF606
- ✅ 无复杂路径
- ✅ 无布尔运算
- ✅ 圆角简单:25px 统一圆角
结论:用代码绘制 ✅
iOS 代码:
// ✅ 推荐:用代码绘制(0KB,易维护)
private lazy var maskView: UIView = {
let v = UIView()
v.backgroundColor = MKUIStyle.mk_cXX() /* color_map: #FFF606 */ // 黄色
v.layer.cornerRadius = 25
v.layer.cornerCurve = .continuous // 平滑圆角(iOS 13+)
return v
}()
iOS 12 兼容:
// iOS 12 不支持 .continuous,使用普通圆角
v.layer.cornerRadius = 25
// 如果需要更平滑的效果,可以略微增大圆角值
v.layer.cornerRadius = 27 // 视觉上接近 cornerSmoothing: 0.6
示例 2:渐变背景
Figma 数据:
{
"fills": [{
"type": "GRADIENT_LINEAR",
"gradientStops": [
{"color": {"r": 0, "g": 0.576, "b": 0.933}, "position": 0},
{"color": {"r": 0.118, "g": 0.396, "b": 0.859}, "position": 1}
]
}]
}
iOS 代码:
// ✅ 推荐:用 CAGradientLayer(0KB)
private func setupGradient() {
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [
MKUIStyle.mk_cXX() /* color_map: #0093EE */.cgColor, // 起始色
MKUIStyle.mk_cXX() /* color_map: #1E65DC */.cgColor // 结束色
]
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5) // 左
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5) // 右
backgroundView.layer.insertSublayer(gradientLayer, at: 0)
}
override func layoutSubviews() {
super.layoutSubviews()
// 更新渐变层大小
backgroundView.layer.sublayers?.first?.frame = backgroundView.bounds
}
规则 2:下载为图片(必须)
适用场景
以下 Figma 节点类型必须下载为图片 Assets:
| Figma 类型 | 识别特征 | 原因 | 示例 |
|---|---|---|---|
| 复杂贝塞尔曲线 | 子路径 ≥ 3 个 | 代码实现成本高,易出错 | 美术字体、Logo |
| 布尔运算 | BOOLEAN_OPERATION(UNION/SUBTRACT/INTERSECT/EXCLUDE) | 需要精确像素级控制 | 特殊形状、镂空图形 |
| 图片填充 | fills[0].type == "IMAGE" | 本身就是图片 | 背景图片、照片 |
| 复杂渐变 | 多个渐变点(≥3 个)、径向渐变 + 复杂形状 | iOS 原生渐变限制 | 艺术效果 |
| 阴影/模糊复杂 | 多层阴影、特殊混合模式 | 代码难以完美复现 | 特效文字 |
| 设计师明确标注 | Figma 评论中要求"图片形式" | 尊重设计意图 | - |
判断标准(下载图片)
满足以下任一条件时,下载为图片:
❌ 布尔运算(BOOLEAN_OPERATION)
❌ 贝塞尔曲线路径 ≥ 3 个
❌ 图片填充(fills[0].type == "IMAGE")
❌ 复杂渐变(≥ 3 个渐变点)
❌ 特殊效果(阴影叠加、混合模式)
❌ 美术字体、Logo、品牌图形
❌ 评论中明确要求
示例 3:形状结合(美术字体)
Figma 数据:
{
"id": "1:310",
"name": "形状结合",
"type": "BOOLEAN_OPERATION",
"booleanOperation": "EXCLUDE",
"children": [
{"id": "1:311", "name": "路径", "type": "VECTOR"},
{"id": "1:312", "name": "路径", "type": "VECTOR"},
... // 共 14 个子路径
],
"fills": [{"type": "SOLID", "color": {...}}]
}
判断:
- ❌ 布尔运算:
EXCLUDE(排除) - ❌ 贝塞尔曲线路径:14 个
- ❌ 用途:美术字体"找人试音"
结论:必须下载为图片 ✅
下载步骤(已由 figma-ios-preload-data 完成):
- 设计师点了 export →
node.export.is_export_asset=true→ 数据包阶段 1 已下载 - 设计师没点 export 但代码绘制不可行 → 在 README 标注 TODO,请设计补 export 后重新跑数据包
- 阶段 2 直接
UIImage(named: assets/ios/manifest.items[node_id].asset_name)
iOS 代码:
// ✅ 正确:使用图片 Assets
private lazy var textShapeImageView: UIImageView = {
let v = UIImageView()
v.contentMode = .scaleAspectFit
v.image = UIImage(named: "img_99a99_button_text_shape")
return v
}()
规则 3:边界情况决策
情况 1:圆角矩形(cornerRadius > 50)
判断:
- 如果
cornerRadius接近宽度或高度的 50%(类似胶囊形状) - 且无其他复杂效果
决策:仍然用代码绘制 ✅
// 胶囊形状
v.layer.cornerRadius = v.bounds.height / 2
情况 2:仅一个边有圆角
Figma 特征:
{
"rectangleCornerRadii": [10, 10, 0, 0] // 仅顶部圆角
}
iOS 实现:
// iOS 11+
let path = UIBezierPath(
roundedRect: bounds,
byRoundingCorners: [.topLeft, .topRight],
cornerRadii: CGSize(width: 10, height: 10)
)
let mask = CAShapeLayer()
mask.path = path.cgPath
v.layer.mask = mask
决策:用代码绘制 ✅(略微复杂,但仍可控)
情况 3:简单 Logo(≤2 个路径)
判断:
- 虽然是矢量图形,但路径数量少
- 用途明确为 Logo 或品牌标识
决策:下载为图片 ✅(保证品牌一致性)
情况 4:Icon(iconfont 可用)
判断:
- 设计稿中使用
iconfont/icon_xxx_24命名 - 项目 iconfont 库中有对应图标
决策:用 iconfont ✅(详见 figma-ios-iconfont-mapping)
// 使用 iconfont(最优)
iconLabel.font = MKIconFont.iconFont(withSize: 24) // iconfont_map.font
iconLabel.text = MKIconFont.shared().mk_icon_xxx // iconfont_map.text
实施步骤
步骤 1:从数据包读节点
node = design["nodes"][node_id]
node_type = node["type"] # RECTANGLE / VECTOR / BOOLEAN_OPERATION / ...
fills = node.get("fills", []) # [{"type":"SOLID","color":"rgba(...)"}, ...]
strokes = node.get("strokes", [])
corner_radius = node.get("corner_radius", 0)
children_count = len(node.get("children", []))
boolean_op = node.get("boolean_operation") # UNION / SUBTRACT / INTERSECT / EXCLUDE
is_export = (node.get("export") or {}).get("is_export_asset", False)
步骤 2:应用决策树
def should_download_as_image(node):
if (node.get("export") or {}).get("is_export_asset"):
return True
if any((f.get("type") == "IMAGE") for f in node.get("fills", [])):
return True
if node.get("type") == "BOOLEAN_OPERATION":
return True
if len(node.get("children", [])) >= 3:
return True
for f in node.get("fills", []):
if f.get("type") in ("GRADIENT_LINEAR", "GRADIENT_RADIAL") \
and len(f.get("gradient_stops", [])) >= 3:
return True
name = node.get("name", "").lower()
if any(k in name for k in ["logo", "美术", "艺术", "形状结合", "shape"]):
return True
return False
该节点已被
figma-ios-preload-data标记_role.is_export_asset时,imageset 已经在assets/ios/,直接UIImage(named: ...)。
步骤 3:生成对应代码
场景 A:用代码绘制
// Figma 节点 1:306 - Mask(黄色圆角矩形)
// 决策: 简单矩形 + 纯色 + 统一圆角 → 用代码绘制 ✅
private lazy var maskView: UIView = {
let v = UIView()
v.backgroundColor = MKUIStyle.mk_cXX() /* color_map: #FFF606 */
v.layer.cornerRadius = 25
v.layer.cornerCurve = .continuous // iOS 13+
return v
}()
场景 B:下载为图片
// Figma 节点 1:310 - 形状结合(美术字体)
// 决策: BOOLEAN_OPERATION + 14个路径 → 下载为图片 ✅
// Assets: img_99a99_button_text_shape
private lazy var textShapeImageView: UIImageView = {
let v = UIImageView()
v.contentMode = .scaleAspectFit
v.image = UIImage(named: "img_99a99_button_text_shape")
return v
}()
代码注释规范
必须包含的信息
// Figma 节点 <node_id> - <节点名称>
// 决策: <判断依据> → <实现方式> ✅
// [如果是图片] Assets: <asset_name>
// [如果是代码] 实现: <UIView/CALayer/CAGradientLayer>
示例
// ✅ 代码绘制示例
// Figma 节点 1:289 - 矩形(步骤指示器背景)
// 决策: 线性渐变 + 边框 + 圆角 → 用代码绘制(CAGradientLayer)✅
// 实现: CAGradientLayer + layer.borderWidth
private lazy var backgroundView: UIView = {
let v = UIView()
v.layer.cornerRadius = 16
v.layer.borderWidth = 1
v.layer.borderColor = UIColor.white.withAlphaComponent(0.15).cgColor
return v
}()
// ✅ 图片下载示例
// Figma 节点 1:310 - 形状结合("找人试音"美术字)
// 决策: BOOLEAN_OPERATION(EXCLUDE) + 14个路径 → 下载为图片 ✅
// Assets: img_99a99_button_text_shape (2KB)
// 尺寸: 85×19
private lazy var textShapeImageView: UIImageView = {
let v = UIImageView()
v.contentMode = .scaleAspectFit
v.image = UIImage(named: "img_99a99_button_text_shape")
return v
}()
优势对比
用代码绘制 ✅
优势:
- ✅ 包体积:0KB
- ✅ 可维护性:易于调整颜色、尺寸、圆角
- ✅ 适配性:支持动态颜色(深色模式)、动态尺寸
- ✅ 性能:矢量缩放,无失真
劣势:
- ⚠️ 开发成本:需要写代码
- ⚠️ 限制:复杂图形无法实现
下载为图片 ✅
优势:
- ✅ 准确性:100% 还原设计稿
- ✅ 复杂度:支持任意复杂图形
- ✅ 开发速度:直接使用 UIImage
劣势:
- ⚠️ 包体积:每个图片 1-10KB
- ⚠️ 维护成本:修改需要重新导出
- ⚠️ 适配性:深色模式需要两套图
常见错误
❌ 错误 1:简单图形也下载为图片
// ❌ 不推荐:纯色圆角矩形下载为图片(浪费 2KB)
maskImageView.image = UIImage(named: "img_xxx_mask")
// ✅ 推荐:用代码绘制(0KB)
v.backgroundColor = MKUIStyle.mk_cXX() /* color_map: #FFF606 */
v.layer.cornerRadius = 25
❌ 错误 2:复杂图形用代码硬写
// ❌ 不推荐:14 个贝塞尔路径用代码手写(易出错)
let path = UIBezierPath()
path.move(to: CGPoint(x: 155, y: 700))
path.addCurve(to: ..., controlPoint1: ..., controlPoint2: ...)
// ... 100+ 行代码
// ✅ 推荐:下载为图片(2KB,准确)
textImageView.image = UIImage(named: "img_xxx_text_shape")
❌ 错误 3:忘记 iOS 12 兼容
// ❌ iOS 12 崩溃
v.layer.cornerCurve = .continuous // iOS 13+
// ✅ iOS 12 兼容
if #available(iOS 13.0, *) {
v.layer.cornerCurve = .continuous
}
❌ 错误 4:属性名与系统冲突
// ❌ 编译错误:UIView 已有只读属性 maskView
private lazy var maskView: UIView = { ... }()
// ✅ 正确:使用描述性名称避免冲突
private lazy var yellowMaskBackgroundView: UIView = { ... }()
禁止使用的属性名(与 UIKit 冲突):
- ❌
backgroundView- UICollectionViewCell 已有此属性(iOS 14+,改用cellBackgroundView) - ❌
maskView- UIView 已有此属性 - ❌
layer- UIView 已有此属性 - ❌
frame- UIView 已有此属性 - ❌
bounds- UIView 已有此属性 - ❌
superview- UIView 已有此属性 - ❌
subviews- UIView 已有此属性 - ❌
backgroundColor- UIView 已有此属性(除非 override) - ❌
isHidden- UIView 已有此属性(除非 override)
推荐命名方式:
- ✅
yellowMaskBackgroundView- 描述性 + 具体用途 - ✅
buttonMaskView- 前缀 + 描述 - ✅
customMaskLayer- custom 前缀 - ✅
maskBackgroundView- 组合描述
完整案例:性别选择器按钮
Figma 节点信息
节点 1:379("不限" 背景 - 已选中状态)
design.json[1:379](数据包字段,颜色已规范化为 rgba(...) 字符串;为了便于对照,下面同时展示 Figma 原始 fills 结构):
{
"id": "1:379",
"name": "Rectangle 9备份 3",
"type": "VECTOR",
"fills": [
{
"opacity": 0.1,
"type": "SOLID",
"color": {"r": 1.0, "g": 0.9647, "b": 0.0235} // #FFF606
}
],
"strokes": [
{
"type": "SOLID",
"color": {"r": 1.0, "g": 0.9647, "b": 0.0235} // #FFF606
}
],
"strokeWeight": 1.0,
"strokeAlign": "INSIDE",
"cornerRadius": 8.0
}
节点 1:382("男生" 背景 - 未选中状态)
{
"id": "1:382",
"name": "Rectangle 9备份 3",
"type": "VECTOR",
"fills": [
{
"opacity": 0.1,
"type": "SOLID",
"color": {"r": 1.0, "g": 1.0, "b": 1.0} // #FFFFFF
}
],
"strokes": [
{
"visible": false,
"type": "SOLID"
}
],
"strokeWeight": 0.0,
"cornerRadius": 8.0
}
判断:用代码绘制 ✅
检查清单:
✅ 形状简单:矩形
✅ 填充简单:纯色 SOLID(#FFF606 / #FFFFFF)
✅ 无复杂路径
✅ 无布尔运算
✅ 圆角简单:8px 统一圆角
✅ 无复杂效果(effects 全部 visible=false)
✅ 尺寸固定:68×32
结论: 必须用代码绘制,禁止下载图片!
实现代码
private class GenderButton: UIControl {
private let buttonTitle: String
private var isButtonSelected: Bool
/// 背景视图(代码绘制)
/// Figma 节点 1:379(已选中)/ 1:382(未选中)
private lazy var genderBackgroundView: UIView = {
let v = UIView()
v.layer.cornerRadius = 8 // Figma cornerRadius
v.layer.masksToBounds = true
v.isUserInteractionEnabled = false
return v
}()
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.textAlignment = .center
return label
}()
init(title: String, isSelected: Bool) {
self.buttonTitle = title
self.isButtonSelected = isSelected
super.init(frame: .zero)
setupUI()
setupConstraints()
updateAppearance()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
addSubview(genderBackgroundView)
addSubview(titleLabel)
titleLabel.text = buttonTitle
}
private func setupConstraints() {
genderBackgroundView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
titleLabel.snp.makeConstraints { make in
make.center.equalToSuperview()
}
}
private func updateAppearance() {
if isButtonSelected {
// Figma 节点 1:379 - 已选中背景 / 边框 / 文字 → 查 color_map + font_map
genderBackgroundView.backgroundColor = MKUIStyle.mk_c26_10() /* 示例:以 color_map 为准 */
genderBackgroundView.layer.borderColor = MKUIStyle.mk_c26().cgColor
genderBackgroundView.layer.borderWidth = 1.0
titleLabel.textColor = MKUIStyle.mk_c26()
titleLabel.font = MKUIStyle.mk_f14_m()
} else {
// Figma 节点 1:382 - 未选中
genderBackgroundView.backgroundColor = UIColor(white: 1.0, alpha: 0.1) /* 或 color_map 命中值 */
genderBackgroundView.layer.borderWidth = 0
titleLabel.textColor = MKUIStyle.mk_c12_80() /* 示例:以 color_map 为准 */
titleLabel.font = MKUIStyle.mk_f14()
}
}
}
为什么不下载图片?
如果下载图片会遇到的问题:
-
❌ 图片格式 / 体积代价:简单形状若也走切图,需要导出多倍图(@2x/@3x)才能保证清晰度,包体积无谓增大;而 iOS 原生不支持 SVG,无法直接复用 Figma 的矢量数据
-
❌ 无法动态修改颜色
- 设计稿改了颜色,需要重新下载图片
- 无法适配深色模式
-
❌ 增加维护成本
- 每个状态需要 2 张图片(@2x + @3x)
- 修改圆角/边框需要重新制作图片
用代码绘制的优势:
- ✅ 零体积:不增加包大小
- ✅ 动态修改:可以改颜色、圆角、边框
- ✅ 适配各种尺寸:不会模糊
- ✅ 性能更好:不需要加载图片资源
- ✅ 维护简单:修改一行代码即可
总结
决策原则
- 简单图形 → 代码绘制(矩形、圆、渐变)
- 复杂图形 → 下载图片(布尔运算、多路径、美术字)
- 有 iconfont → 用 iconfont(最优)
- 有疑问 → 优先代码,实在不行再下载
- 判断仅依赖数据包字段(type / fills / corner_radius / vector_geometry / export.is_export_asset),不再调 MCP 或 Figma REST API
优先级
iconfont > 代码绘制 > 下载图片
验收标准
- ✅ 简单矩形、圆角不能下载为图片
- ✅ 布尔运算、≥3 路径必须下载为图片
- ✅ 所有代码绘制必须标注决策理由
- ✅ 所有下载图片必须使用语义化命名
- ✅ iOS 12 兼容性检查
- ✅ 下载图片前必须检查文件类型(PNG/SVG)