Figma ios uicollectionview codegen
Skill mythkiven/figma-ios-codegen/.cursor/skills/figma-ios-uicollectionview-codegen
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-uicollectionview-codegenAssembled 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 节点数据生成完整的 UICollectionView Swift 代码。 包括:分析 Cell 子元素 → 推断模型字段 → 生成 Mock 数据 → 生成 CollectionView/Cell 代码 → 生成交互逻辑(选中态/日志)。 Use when generating UICollectionView code from Figma designs.
SKILL.md
26.3 KB, as published. Nobody here has run it
UICollectionView 代码生成
⚡ QUICK_REF(Agent 优先读此处)
核心定位:本 SKILL 提供"流程框架 + 指导原则 + 强制约束",具体如何分析和生成代码由大模型根据实际场景判断。
本 skill 的定位:阶段 2 基础 UI 代码生成(UI + 临时模型 + Mock 数据 + 交互骨架),阶段 3 会用 PRD + 接口文档把 mock 换成真实接口、临时模型换成 API 模型,本 skill 不管阶段 3。
核心流程(6 步):
- 分析 Cell 结构 → 遍历所有子节点,识别布局和元素
- 推断临时模型字段 → 根据语义推断阶段 2 临时 struct(阶段 3 会用真实 API 模型替换)
- 生成 Mock 数据 → 阶段 2 强制(图片走
manifest.json.mock.default_image_url,其他字段语义生成) - 生成 CollectionView → lazy var + layout + DataSource
- 生成 Cell 代码 → 子视图 + 布局(SnapKit)+ configure 方法
- 生成交互逻辑 → didSelectItemAt(有选中态 → toggle;无 → print +
// TODO(阶段3): 补业务跳转/网络请求)
生成内容清单
| 组件 | 说明 | 必须 |
|---|---|---|
| Model 定义 | 根据子元素推断字段 | ✅ |
| Mock 数据 | 从 Figma 提取或生成占位 | ✅ |
| CollectionView | lazy var + layout 配置 | ✅ |
| Cell 定义 | configure(item:) 方法 | ✅ |
| DataSource | numberOfItemsInSection + cellForItemAt | ✅ |
| Delegate | didSelectItemAt 交互逻辑 | ✅ |
| LeftAlignedFlowLayout | 标签集合专用(可选) | ⚠️ |
强制约束(质量保证)
✅ 必须遵守:
- 模型必须包含
id字段 - 必须使用 Model,禁止字符串数组
- 颜色/字体走 bindings map(未命中用 UIKit fallback)
- 布局按 host.layout_engine(默认相对约束;可用 SnapKit 或原生 Auto Layout)
- 交互逻辑:有
isSelected→ toggle + reload;无 → print - 访问级别匹配:DataSource / Delegate 的 extension 访问级别必须等于宿主类。详见下方「访问级别匹配规则」。
❌ 严格禁止:
- 禁止跳过流程步骤
- 禁止硬编码数据(必须从 Figma 提取或根据语义生成)
- 禁止同时使用
estimatedItemSize和sizeForItemAt - 禁止生成不完整的代码
- 禁止
public class配extension(默认 internal)的搭配 —— 必报「Method must be declared public」编译错误。
访问级别匹配规则(强制,避免协议一致性报错)
报错形态(这是本规则要根除的):
Method 'collectionView(_:numberOfItemsInSection:)' must be declared public
because it matches a requirement in public protocol 'UICollectionViewDataSource'
原因:public 类实现 public 协议,方法访问级别必须 ≥ public,但 extension 默认是 internal。
另一常见报错(同文件内独立 UICollectionViewCell 子类 + configure(item:)):
Method must be declared fileprivate because its parameter uses a private type
原因:数据模型写成 private struct FooItem,而 Cell 的 func configure(item: FooItem) 默认为 internal。方法的可见性不能高于签名中出现的类型——private 类型只能出现在 private / fileprivate 成员签名里。
处置(二选一,推荐前者):
| 方案 | 写法 |
|---|---|
| A(推荐) | 模型改为 internal:struct FooItem { ... }(去掉 private)。同文件 VC + Cell 共用,仍为模块内封装,不污染其他文件除非 public。 |
| B | 保留 private struct,则 Cell 上写 fileprivate func configure(item: FooItem)(或把整个 Cell 标 fileprivate final class)。 |
生成约定:凡是「VC 私有数组元素类型」又要传给「同文件 Cell.configure」的 Model,不要用 private struct,直接用 internal struct(或 fileprivate struct,与 Cell 方法同级)。
生成前必做:探测宿主基类访问级别
# 在 Agent 生成代码前先扫一次(基类名 = host.bases.*)
rg -n "^(public |open |@objc public |@objc open )?class\s+<BaseClassName>\b" <host_project_root>
rg -n "^@interface\s+<BaseClassName>\b" <host_project_root>
模板选择表
下表中的
XxxVC/XxxCell都是占位符,实际类名必须以业务前缀<P>开头(见 host.json / PROJECT_BINDING §0)。例:AppCategoryListVC/AppCategoryCell。
| 探测结果 | 类声明 | 协议 extension |
|---|---|---|
open class <VC_BASE> | open class <P>XxxVC: <VC_BASE> | extension <P>XxxVC: ... { open func ... } |
public class <VC_BASE> | public class <P>XxxVC: <VC_BASE> | public extension <P>XxxVC: ... { ... } |
class <VC_BASE>(internal) | class <P>XxxVC: <VC_BASE> | extension <P>XxxVC: ... { ... } |
OC @interface <VC_BASE> | class <P>XxxVC: <VC_BASE> | extension <P>XxxVC: ... { ... } |
| 探测失败 | class <P>XxxVC: <VC_BASE>(保守 internal) | extension <P>XxxVC: ... { ... } |
推荐模板(90% 场景:基类是 internal 或 OC)
class AppCategoryListViewController: UIViewController {
private var categoryRows: [CategoryRow] = []
}
// MARK: - UICollectionViewDataSource
extension AppCategoryListViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return categoryRows.count
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: AppCategoryCell.reuseId,
for: indexPath
) as! AppCategoryCell
cell.configure(categoryRows[indexPath.item])
return cell
}
}
仅当基类是 public 时才用此模板
public class AppCategoryListViewController: UIViewController {
public private(set) var categoryRows: [CategoryRow] = []
}
// MARK: - UICollectionViewDataSource —— 整个 extension 加 public
public extension AppCategoryListViewController {
func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int { ... }
}
注:把协议一致性写在主类定义里(
class XxxVC: <VC_BASE>, UICollectionViewDataSource),extension 仅放方法,写public extension XxxVC { ... }最简洁,不用重复一致性声明。
使用场景
当 figma-ios-listview-recognition 返回识别结果后,调用本 SKILL 生成代码。
输入:
- 识别结果 dict(来自
figma-ios-listview-recognition) - 数据包
design.json+comments.json+assets/ios/manifest.json+manifest.json(含mock.default_image_url) - 优先读列表容器上的预算字段(阶段 1 已写好,勿重算):
design.json[list_node]._role.is_list_containerdesign.json[list_node]._layout_hint.list→{ scroll_axis, cell_template_node_id, cell_count }index.json.by_role.list_container/audit.json.list_containers
输出:
- Swift 阶段 2 基础 UI 代码(完整 UICollectionView 实现 + 临时模型 + Mock 数据 +
// TODO(阶段3)锚点)
步骤 0:从 _layout_hint.list 定模板与滚动轴
hint = design.json[list_node_id]._layout_hint.list
cell_root = nodes[hint.cell_template_node_id] # Cell 结构分析的根
scroll_axis = hint.scroll_axis # "horizontal" | "vertical"
cell_count = hint.cell_count # Mock 条数上限参考
若 _layout_hint.list 缺失,再 fallback 到 listview-recognition 的识别结果 dict。
步骤 1:分析 Cell 结构
目标
分析 Cell 的所有子节点,识别其结构、布局模式和所有元素。
执行指导
分析内容:
- 遍历 Cell 的所有子节点(包括深层嵌套节点)
- 识别所有节点类型(TEXT/IMAGE/FRAME/VECTOR/COMPONENT/等)
- 记录每个节点的:
- 位置信息(x, y, width, height)
- 样式信息(字号/颜色/填充等)
- 语义信息(节点名称/评论标注)
- 识别布局模式(垂直/水平/网格/复杂布局)
注意事项:
- ⚠️ 不要限制遍历深度:Cell 可能有多层嵌套,应遍历完整子树
- ⚠️ 不要只识别特定类型:识别所有节点类型,包括容器节点(FRAME/GROUP)
- ⚠️ 识别特殊场景:
- Cell 中有输入框(TextField/TextArea)
- Cell 中嵌套了列表(识别为嵌套 CollectionView)
- Cell 有多个状态(从评论或节点名判断)
输出
一个内部分析结构(非代码产物),至少覆盖:所有子节点的 node_id / type / name / 几何 / 样式、整体 layout_pattern(vertical / horizontal / grid / complex)、special_cases 标记(has_input / has_nested_list / has_states 等)。用于驱动后续步骤。
步骤 2:推断/映射模型字段
目标
根据 Cell 结构和可用的模型信息,确定模型字段名和类型。
⚠️ 注意:本步骤支持两种模式,根据输入决定使用哪种模式。
推断临时模型(阶段 2 唯一模式)
📌 阶段 2 只做临时模型(从 Figma 语义推断)。阶段 3 会把临时模型替换为接口文档对应的真实 API 模型——那不是本 skill 的职责。
场景:只有 Figma 设计稿,没有真实 API 模型(阶段 2 的默认状态)
推断指导原则(优先级从高到低):
1. 评论标注(最高优先级)
- 如果评论中明确标注了字段名(如"字段:price"),直接使用
- 如果评论标注了"选中态",生成
isSelected: Bool - 如果评论标注了"状态:A/B/C",生成
enum State
2. 节点名称语义
- 节点名包含"价格"/"price"/"金额"/"¥" →
price: String - 节点名包含"时间"/"time"/"日期"/"date" →
timestamp: String - 节点名包含"数量"/"count"/"点赞"/"评论" →
count: Int - 节点名包含"状态"/"status" →
status: String - 节点名包含"标签"/"tag" →
tags: [String] - 节点名包含"头像"/"avatar" →
avatar: String - 更多语义可自行推断...
3. 样式特征辅助
- 字号最大的 TEXT →
title: String - 字号第二大的 TEXT →
subtitle: String - 颜色特殊(红色/黄色) →
highlightText: String(或根据语义命名)
4. 位置关系辅助
- 位于顶部的 TEXT → 可能是标题
- 位于底部的 TEXT → 可能是描述或时间
- 位于左侧的 IMAGE → 可能是头像或图标
5. 兜底规则
- 无法判断时,使用通用命名:
text1,text2,image1,image2等 - 或者根据节点名直接作为字段名(去除特殊字符)
字段类型指导
| 场景 | 字段类型 | 示例 |
|---|---|---|
| TEXT 节点 | String | let title: String |
| IMAGE 节点 | String | let icon: String |
| 评论标注"选中态" | Bool | var isSelected: Bool = false |
| 节点名包含"数量"/"计数" | Int | let count: Int |
| 节点名包含"标签" | [String] | let tags: [String] |
| 评论标注"状态:A/B/C" | enum | var state: State = .normal |
| TextField 节点 | String(可编辑) | var inputText: String |
强制规则
- ✅ 必须:第一个字段必须是
id: String(唯一标识) - ✅ 必须:所有字段都要有注释说明用途
- ⚠️ 建议:字段名使用驼峰命名法,语义清晰
输出格式(示例):
struct CategoryModel {
let id: String // 唯一标识
let title: String // 品类名称
let icon: String // 品类图标(本地 asset_name 或 http URL)
var isSelected: Bool = false // 选中态(有需求时才加)
}
关于阶段 3 的 API 模型映射(超出本 skill 范围)
阶段 3 拿到接口文档后会把临时模型替换为真实 API 模型(例如 title → productName、icon → iconUrl)。这一步由阶段 3 另行完成,本 skill 不生成 基于 API 模型的代码,只生成临时模型 + mock 数据 + // TODO(阶段3) 锚点;阶段 3 沿着锚点 grep 后做替换。
步骤 3:生成 Mock 数据(阶段 2 强制)
目标
为模型生成合理的占位数据,让阶段 2 代码开箱可跑、可预览。阶段 3 会用接口文档替换这些 mock。
📌 阶段 2 必须生成 mock 数据——这是阶段 2 的定位(UI 能跑能看),不是"偷懒"。阶段 3 的任务之一就是把 mock 换成真实数据。
数据来源优先级
-
优先级 1:提取实际子节点数据
- 如果父节点有多个实际子节点(非 SYMBOL),从这些节点提取数据
-
优先级 2:根据字段语义生成占位数据
- 如果只有 SYMBOL 节点(内容未展开),根据字段名推断占位内容
-
优先级 3:使用评论指定的数据
- 如果评论中指定了 Mock 数据,优先使用
-
图片字段(任一情况):服务器下发图 →
manifest.json.mock.default_image_url+// TODO(阶段3): 接入业务接口
Mock 数据生成指导
根据字段名推断占位内容:
| 字段名关键词 | 占位数据示例 |
|---|---|
price / 价格 / 金额 | "¥99.00", "¥199.00", "¥299.00" |
time / 时间 / timestamp | "10:30", "昨天", "2026-01-01" |
count / 数量 / 点赞 | "100", "999+", "1.2k" |
status / 状态 | "待支付", "已发货", "已完成" |
avatar / 头像 | 走服务器下发图 → 统一用 manifest.json.mock.default_image_url(禁止写死 img_avatar_1 等假名) |
nickname / 昵称 | "张三", "李四", "王五" |
title / 标题 | 根据场景推断(品类/商品/订单等) |
icon / image / iconUrl / imageUrl | 见下方"图片字段处理策略" |
tags / 标签 | ["热门", "新品"], ["包邮", "促销"] |
图片字段处理策略
数据来源(仅数据包,禁止再调 API):
- 本地切图(首选):
{data_dir}/assets/ios/manifest.jsonmanifest.items[node_id].asset_name→UIImage(named: ...)
- 服务器下发图片(含头像/封面/品类图等 INSTANCE 或 IMAGE fill):节点没在
manifest.items里- 统一用
{data_dir}/manifest.json→mock.default_image_url,配sd_setImage+ TODO 注释 - 详见 figma-ios-image-assets-download「服务器下发图片场景」
- ⛔ 禁止退化成
image = nil+ 纯色占位 - ⛔ 禁止编造
img_avatar_1/img_game_pubg/img_placeholder_1等不存在的 asset 名
- 统一用
生成 Mock 数据时的查找逻辑:
import json, os
def get_image_for_node(node_id: str, data_dir: str) -> str:
# 1) 本地切图优先
assets_manifest = os.path.join(data_dir, "assets/ios/manifest.json")
if os.path.exists(assets_manifest):
with open(assets_manifest) as f:
items = json.load(f).get("items", {})
if node_id in items:
return items[node_id]["asset_name"]
# 2) 没有切图 → 服务器下发图,统一用数据包的 mock URL(禁止编造 asset 名)
with open(os.path.join(data_dir, "manifest.json")) as f:
return json.load(f)["mock"]["default_image_url"]
示例 1:服务器下发图片(节点是 IMAGE 但本地无切图)
// 服务器图统一用数据包的 mock URL(manifest.json → mock.default_image_url)
// ⛔ 严禁写 Figma S3 URL(24h 失效)/ example.com 等假 URL
// 详见 figma-ios-image-assets-download skill「服务器下发图片场景」
CategoryModel(
id: "1",
title: "绝地求生",
icon: "https://picsum.photos/200" // TODO(阶段3): 接入业务接口后替换为真实图片 URL
)
示例 2:本地切图(节点已 export,imageset 已下到 assets/ios/)
// Mock 数据使用 Assets 名称
CategoryModel(
id: "1",
title: "步骤分隔",
icon: "img_8b86f_bitmap_03"
)
示例 3:无法识别来源时的兜底
兜底仍然用 mock URL,禁止退化成编造的 asset 名:
CategoryModel(
id: "1",
title: "未知",
icon: "https://picsum.photos/200" // TODO(阶段3): 接入业务接口后替换为真实图片 URL
)
数据数量指导:
- 有实际子节点:提取实际数量
- 有 SYMBOL:生成 5-10 条
- 默认:生成 3-5 条
数据多样性:
- 保持数据的多样性,不要所有项都一样
- 如果无法推断,使用
字段名_序号(如"title_1","title_2")
输出示例
// 服务器下发图字段统一用 manifest.json.mock.default_image_url
// ⛔ 禁止编造 img_game_pubg / img_product_1 这类不存在的 asset 名
private static let kMockImageURL = "https://picsum.photos/200"
private var categories: [CategoryModel] = [
CategoryModel(id: "1", title: "绝地求生", icon: kMockImageURL, isSelected: false),
CategoryModel(id: "2", title: "LOL", icon: kMockImageURL, isSelected: false),
CategoryModel(id: "3", title: "王者荣耀", icon: kMockImageURL, isSelected: false),
]
步骤 4:生成 CollectionView 代码
目标
生成 UICollectionView 的定义和 DataSource 实现。
代码生成指导
CollectionView 配置:
- 根据识别结果设置
scrollDirection(horizontal/vertical) - 根据识别结果设置
isScrollEnabled(true/false) - 设置
itemSize或使用estimatedItemSize(自动计算) - 设置间距(
minimumLineSpacing/minimumInteritemSpacing)
Layout 选择:
- 标准列表:
UICollectionViewFlowLayout - 标签集合(左对齐换行):
LeftAlignedFlowLayout(见后文) - 复杂布局:可自定义 Layout
DataSource 实现:
numberOfItemsInSection→ 返回数据数组的 countcellForItemAt→ dequeue Cell 并调用configure(item:)
必须遵守
- ✅ 使用
lazy var定义 CollectionView - ✅ 必须实现
UICollectionViewDataSource - ✅ 注释标注 Figma 节点 ID 和名称
- ✅ 如果页面有多个 CollectionView,使用
if collectionView == xxx区分
步骤 5:生成 Cell 代码
目标
生成 UICollectionViewCell 子类,包括子视图定义、布局和 configure(item:) 方法。
代码生成指导
子视图定义:
- 根据 Cell 结构生成对应的子视图(UILabel/UIImageView/UIButton/等)
- 使用闭包初始化子视图,设置默认属性
布局约束:
- 必须使用相对约束(按 host.layout_engine)
- 优先相对布局(而不是绝对坐标)
- 根据布局模式选择:
- 简单布局:直接用相对约束
- 复杂布局:可以使用 UIStackView 辅助
- 嵌套列表:生成嵌套的 UICollectionView
configure(item:) 方法:
- 将模型字段映射到子视图属性
- TEXT 字段 →
label.text = item.xxx - IMAGE 字段 →
imageView.image = UIImage(named: item.xxx) - Bool 字段 → 更新外观(背景色/边框/透明度等)
必须遵守
- ✅ 使用
private class定义 Cell - ✅ 颜色/字体走 bindings map
- ✅ 布局用相对约束(按 host.layout_engine)
- ✅ 必须实现
configure(item:)方法
图片加载处理
根据图片来源自动选择加载方式:
// 需要导入图片库
// 远程图按 host.image_load_template,勿写死宿主图片库 import
func configure(item: CategoryModel) {
titleLabel.text = item.title
// 自动判断图片来源
if item.icon.hasPrefix("http") {
// 在线图片 - 按 host.image_load_template
iconImageView.sd_setImage(with: URL(string: item.icon), placeholderImage: UIImage(named: "img_placeholder"))
} else {
// 本地图片 - 直接加载
iconImageView.image = UIImage(named: item.icon)
}
// 根据选中状态更新外观(如果有)
contentView.alpha = item.isSelected ? 1.0 : 0.6
}
注意:
- 远程图按
host.image_load_template(默认可用sd_setImage);不要写死宿主图片库类名 - 服务器下发图片统一用
{data_dir}/manifest.json的mock.default_image_url - ⛔ 严禁 在 Mock 数据中直接写
https://figma-alpha-api.s3.*临时 URL(24h 失效) - ⛔ 严禁 写
example.com/placeholder.com等假 URL(运行时只能看到失败占位)
数据来源说明:
- 本地切图(节点
_role.is_export_asset = true):从{data_dir}/assets/ios/manifest.json读取asset_name,UIImage(named:)引用 - 服务器下发图片(节点 fills 含 IMAGE 但未 export):从
{data_dir}/manifest.json读取mock.default_image_url,所有此类字段统一用同一个 URL + TODO 注释 - 详细规范见 figma-ios-image-assets-download「服务器下发图片场景」
步骤 6:生成交互代码
目标
生成 UICollectionViewDelegate 的 didSelectItemAt 实现。
交互逻辑规则
有 isSelected 字段:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// 1. 更新选中状态
categories[indexPath.item].isSelected.toggle()
// 2. 刷新 Cell
collectionView.reloadItems(at: [indexPath])
// 3. 打印日志(可选)
print("选中: \(categories[indexPath.item].title)")
}
无 isSelected 字段:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// 打印日志
let item = categories[indexPath.item]
print("点击了: \(item.title)")
}
必须遵守
- ✅ 必须实现
UICollectionViewDelegate - ✅ 如果页面有多个 CollectionView,使用
if collectionView == xxx区分 - ✅ 有选中态 → 必须 toggle + reload
- ✅ 无选中态 → 打印日志或留空(由业务实现)
LeftAlignedFlowLayout(标签集合专用)
当识别结果 layoutType == 'LeftAlignedFlow' 时,生成此布局类:
// MARK: - LeftAlignedFlowLayout
/// 左对齐流式布局,用于标签集合、可换行列表等场景
private class LeftAlignedFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil }
var leftMargin: CGFloat = sectionInset.left
var maxY: CGFloat = -1.0
let modifiedAttributes = attributes.map { $0.copy() as! UICollectionViewLayoutAttributes }
for attribute in modifiedAttributes {
if attribute.frame.origin.y >= maxY {
leftMargin = sectionInset.left
}
attribute.frame.origin.x = leftMargin
leftMargin += attribute.frame.width + minimumInteritemSpacing
maxY = max(attribute.frame.maxY, maxY)
}
return modifiedAttributes
}
}
常见场景处理
每个场景给出识别信号 + 必须遵守的实现规则,代码细节由 LLM 按场景展开。
场景 1:Cell 中有输入框
- 识别:节点类型为
INPUT,或节点名含输入/input - 规则:生成
UITextField(字体/色走 bindings map),configure(item:)里不要赋text(保持可编辑状态),placeholder用 Figma 原文
场景 2:Cell 中嵌套 CollectionView
- 识别:Cell 内部又命中
list_container/chip_group_hits - 规则:在 Cell 内 lazy 一个子
UICollectionView+ 单独实现UICollectionViewDataSource(extension 挂在 Cell 类上);标签集合用LeftAlignedFlowLayout
场景 3:Cell 有多种状态
- 识别:评论标注「状态:A/B/C」或节点名含
状态/state - 规则:定义
enum State,模型里var state: State,configure内switch item.state更新外观;禁止用多个 Bool 模拟互斥状态
场景 4:Cell 元素 ≥5 或布局不规则
- 规则:用
UIStackView组合行(axis + spacing),或拆多个UIView容器再 SnapKit 嵌套;不要在contentView里平铺 10+ 个 subview 用绝对约束
场景 5:Cell 动态高度
- 识别:TEXT 节点行数/长度不固定
- 规则:layout 设
estimatedItemSize = .automaticSize;Cell 重写preferredLayoutAttributesFitting用systemLayoutSizeFitting(targetSize, horizontal: .required, vertical: .fittingSizeLevel)计算真实高度;禁止与sizeForItemAt共用(见「常见错误」)
常见错误
「字符串数组」「硬编码数据」「编造 asset 名」已在 QUICK_REF 强制约束里列出,此处只列非直觉错误。
❌ 同时使用 estimatedItemSize 和 sizeForItemAt
两者冲突,必须二选一:
- 自适应高度 → 只用
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize,不要实现sizeForItemAt - 固定/手算高度 → 只用
sizeForItemAt,不要设estimatedItemSize
与其他 Skill 的协作
figma-ios-listview-recognition → 识别结果(list_container / chip_group_hits / layoutType)
↓
本 SKILL → Swift 基础 UI 代码(UI + 临时模型 + Mock + // TODO(阶段3))
↓
figma-ios-snapkit-layout → 约束
figma-ios-rxswift-interaction-pattern → 交互骨架(可选)
↓
阶段 3(不在本 skill) → 结合 PRD + 接口文档替换 mock、接入真实 API
临时模型 + Mock 数据是本 skill 的标准产物,不要因为"不够生产级"就跳过或降级为编造 asset 名。
<!-- QUICK_REF 已是总结,不再冗余重复。 -->