agentsclimarketplace

Figma ios uicollectionview codegen

Skill mythkiven/figma-ios-codegen/.cursor/skills/figma-ios-uicollectionview-codegen

根据 Figma 节点数据生成完整的 UICollectionView Swift 代码。 包括:分析 Cell 子元素 → 推断模型字段 → 生成 Mock 数据 → 生成 CollectionView/Cell 代码 → 生成交互逻辑(选中态/日志)。 Use when generating UICollectionView code from Figma designs.From its SKILL.md

Install
npx -y skills add mythkiven/figma-ios-codegen --skill figma-ios-uicollectionview-codegen

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 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.

SKILL.md

26.3 KB, ~8.3k tokens by cl100k_base, as published. Nobody here has run it

UICollectionView 代码生成

⚡ QUICK_REF(Agent 优先读此处)

核心定位:本 SKILL 提供"流程框架 + 指导原则 + 强制约束",具体如何分析和生成代码由大模型根据实际场景判断。

本 skill 的定位:阶段 2 基础 UI 代码生成(UI + 临时模型 + Mock 数据 + 交互骨架),阶段 3 会用 PRD + 接口文档把 mock 换成真实接口、临时模型换成 API 模型,本 skill 不管阶段 3。

核心流程(6 步)

  1. 分析 Cell 结构 → 遍历所有子节点,识别布局和元素
  2. 推断临时模型字段 → 根据语义推断阶段 2 临时 struct(阶段 3 会用真实 API 模型替换)
  3. 生成 Mock 数据 → 阶段 2 强制(图片走 manifest.json.mock.default_image_url,其他字段语义生成)
  4. 生成 CollectionView → lazy var + layout + DataSource
  5. 生成 Cell 代码 → 子视图 + 布局(SnapKit)+ configure 方法
  6. 生成交互逻辑 → didSelectItemAt(有选中态 → toggle;无 → print + // TODO(阶段3): 补业务跳转/网络请求

生成内容清单

组件说明必须
Model 定义根据子元素推断字段
Mock 数据从 Figma 提取或生成占位
CollectionViewlazy var + layout 配置
Cell 定义configure(item:) 方法
DataSourcenumberOfItemsInSection + cellForItemAt
DelegatedidSelectItemAt 交互逻辑
LeftAlignedFlowLayout标签集合专用(可选)⚠️

强制约束(质量保证)

✅ 必须遵守:

  • 模型必须包含 id 字段
  • 必须使用 Model,禁止字符串数组
  • 颜色/字体走 bindings map(未命中用 UIKit fallback)
  • 布局按 host.layout_engine(默认相对约束;可用 SnapKit 或原生 Auto Layout)
  • 交互逻辑:有 isSelected → toggle + reload;无 → print
  • 访问级别匹配:DataSource / Delegate 的 extension 访问级别必须等于宿主类。详见下方「访问级别匹配规则」。

❌ 严格禁止:

  • 禁止跳过流程步骤
  • 禁止硬编码数据(必须从 Figma 提取或根据语义生成)
  • 禁止同时使用 estimatedItemSizesizeForItemAt
  • 禁止生成不完整的代码
  • 禁止 public classextension(默认 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(推荐)模型改为 internalstruct 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_container
    • design.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 节点Stringlet title: String
IMAGE 节点Stringlet icon: String
评论标注"选中态"Boolvar isSelected: Bool = false
节点名包含"数量"/"计数"Intlet count: Int
节点名包含"标签"[String]let tags: [String]
评论标注"状态:A/B/C"enumvar 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 模型(例如 titleproductNameiconiconUrl)。这一步由阶段 3 另行完成,本 skill 不生成 基于 API 模型的代码,只生成临时模型 + mock 数据 + // TODO(阶段3) 锚点;阶段 3 沿着锚点 grep 后做替换。


步骤 3:生成 Mock 数据(阶段 2 强制)

目标

为模型生成合理的占位数据,让阶段 2 代码开箱可跑、可预览。阶段 3 会用接口文档替换这些 mock。

📌 阶段 2 必须生成 mock 数据——这是阶段 2 的定位(UI 能跑能看),不是"偷懒"。阶段 3 的任务之一就是把 mock 换成真实数据。

数据来源优先级

  1. 优先级 1:提取实际子节点数据

    • 如果父节点有多个实际子节点(非 SYMBOL),从这些节点提取数据
  2. 优先级 2:根据字段语义生成占位数据

    • 如果只有 SYMBOL 节点(内容未展开),根据字段名推断占位内容
  3. 优先级 3:使用评论指定的数据

    • 如果评论中指定了 Mock 数据,优先使用
  4. 图片字段(任一情况):服务器下发图 → 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):

  1. 本地切图(首选){data_dir}/assets/ios/manifest.json
    • manifest.items[node_id].asset_nameUIImage(named: ...)
  2. 服务器下发图片(含头像/封面/品类图等 INSTANCE 或 IMAGE fill):节点没在 manifest.items
    • 统一用 {data_dir}/manifest.jsonmock.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 → 返回数据数组的 count
  • cellForItemAt → 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.jsonmock.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_nameUIImage(named:) 引用
  • 服务器下发图片(节点 fills 含 IMAGE 但未 export):从 {data_dir}/manifest.json 读取 mock.default_image_url,所有此类字段统一用同一个 URL + TODO 注释
  • 详细规范见 figma-ios-image-assets-download「服务器下发图片场景」

步骤 6:生成交互代码

目标

生成 UICollectionViewDelegatedidSelectItemAt 实现。

交互逻辑规则

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: Stateconfigureswitch item.state 更新外观;禁止用多个 Bool 模拟互斥状态

场景 4:Cell 元素 ≥5 或布局不规则

  • 规则:用 UIStackView 组合行(axis + spacing),或拆多个 UIView 容器再 SnapKit 嵌套;不要contentView 里平铺 10+ 个 subview 用绝对约束

场景 5:Cell 动态高度

  • 识别:TEXT 节点行数/长度不固定
  • 规则:layout 设 estimatedItemSize = .automaticSize;Cell 重写 preferredLayoutAttributesFittingsystemLayoutSizeFitting(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 已是总结,不再冗余重复。 -->

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.