# Figma Ios Selection Interaction

> >-

- **Type:** Skill
- **Install:** `agentstack add skill-mythkiven-figma-ios-codegen-figma-ios-selection-interaction`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [mythkiven](https://agentstack.voostack.com/s/mythkiven)
- **Installs:** 0
- **Category:** [Content & Media](https://agentstack.voostack.com/c/content-and-media)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [mythkiven](https://github.com/mythkiven)
- **Source:** https://github.com/mythkiven/figma-ios-codegen/tree/main/.cursor/skills/figma-ios-selection-interaction
- **Website:** https://github.com/mythkiven/figma-ios-codegen/tree/main/docs

## Install

```sh
agentstack add skill-mythkiven-figma-ios-codegen-figma-ios-selection-interaction
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Figma 选择交互逻辑生成

## 职责范围

- ✅ 从 `figma-ios-preload-data` 数据包（`design.json`）自动识别选择交互模式（单选/多选/Toggle）
- ✅ 生成选中态变化逻辑（更新数据模型、刷新 UI）
- ✅ 生成 `didSelectItemAt`（列表统一 UICollectionView；**不用** `UITableView` / `didSelectRowAt`）
- ✅ 生成单个按钮 Toggle 逻辑
- ✅ 集成 RxSwift 交互模式（按 `host.interaction`；默认也可 target-action，见 rxswift skill）
- ✅ 保持数据模型与 UI 同步
- ❌ 不处理业务逻辑（网络请求、页面跳转等，留 TODO 注释）
- ❌ 不处理动画效果（由其他 Skill 负责）
- ❌ **不**从 Figma MCP / REST 再拉数据；视觉差异读 `design.json`（或已由 `figma-ios-component-state-recognition` 提取的状态）
- ❌ **不**替代 `figma-ios-component-state-recognition`（外观 / `updateAppearance`）与 `figma-ios-uicollectionview-codegen`（CV 骨架）

---

## 适用场景

| 场景 | Figma 特征 | 生成代码 |
|------|-----------|---------|
| **ListView 单选** | 多个同类节点，只有 1 个选中 | `didSelectItemAt` + 单选互斥逻辑 |
| **ListView 多选** | 多个同类节点，多个选中 | `didSelectItemAt` + Toggle 逻辑 |
| **单个按钮 Toggle** | 单个节点有选中态 | RxSwift tap + Toggle 逻辑 |
| **SegmentedControl** | 多个并列按钮，1 个选中 | 按钮组 + 单选互斥逻辑 |

---

## 识别规则

### 规则 1：ListView 单选模式

**触发条件（必须全部满足）：**

```python
def is_single_selection_listview(figma_nodes):
    """
    判断是否为单选 ListView
    """
    # 1. 节点数量 ≥ 2
    if len(figma_nodes)  本仓列表**一律** UICollectionView。短选项（性别 / 筛选项）也用 CV，不要生成 `UITableView`。

**数据模型：**
```swift
struct GenderItem {
    let id: String
    let title: String
    var isSelected: Bool
}

private var genders: [GenderItem] = [
    GenderItem(id: "1", title: "不限", isSelected: true),
    GenderItem(id: "2", title: "男生", isSelected: false),
    GenderItem(id: "3", title: "女生", isSelected: false)
]
```

**生成交互逻辑：**
```swift
// MARK: - UICollectionViewDelegate

extension GenderSelectionCell: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        // ========== 单选模式：取消所有选中，只选中当前项 ==========
        
        for i in 0.. 1
```

---

### Step 2：统计选中节点数量

```python
def count_selected_nodes(figma_nodes):
    """统计有多少个节点处于选中态（读 design.json）。"""
    return sum(1 for node in figma_nodes if is_node_selected(node, peers=figma_nodes))

def is_node_selected(node, peers=None):
    """
    判断单个节点是否更像「选中态」。读 design.json，不读 Tailwind / MCP。
    启发式（可与 component-state-recognition 对齐）：
    - fills/strokes 的 rgba alpha 更高、或颜色与同伴不同
    - font.weight 更粗（如 ≥500）
    - 名称含 选中/selected/active
    """
    name = (node.get("name") or "").lower()
    if any(k in name for k in ("选中", "selected", "active")):
        return True
    font = node.get("font") or {}
    if (font.get("weight") or 0) >= 500 and peers:
        weights = [(p.get("font") or {}).get("weight") or 400 for p in peers]
        if font.get("weight") == max(weights) and len(set(weights)) > 1:
            return True
    fills = node.get("fills") or []
    for f in fills:
        color = f.get("color") or ""
        # alpha 段：rgba(r,g,b,a) 中 a 明显大于同伴常见「淡选中底」
        if "rgba(" in color and color.rstrip(")").split(",")[-1].strip() not in ("0", "0.0", "1", "1.0"):
            # 有半透明主题底时倾向选中；精确对比交给 peers 差分
            if peers and has_selection_state(peers):
                return True
    return False
```

> 具体主题色以当前稿 `fills[].color` 为准，**不要**写死某业务色值；上表只示意差分思路。

---

### Step 3：判断交互模式

```python
def detect_interaction_mode(figma_nodes):
    """
    综合判断交互模式
    """
    # 1. 检查是否有选中态
    if not has_selection_state(figma_nodes):
        return None
    
    # 2. 统计选中节点数量
    selected_count = count_selected_nodes(figma_nodes)
    
    # 3. 判断是否为列表
    is_list = is_listview(figma_nodes)
    
    # 4. 返回交互模式
    if is_list:
        if selected_count == 1:
            return 'single_selection'  # ListView 单选
        elif selected_count >= 2:
            return 'multiple_selection'  # ListView 多选
        else:
            return None  # 没有默认选中（需要手动处理）
    
    else:
        # 单个节点
        if len(figma_nodes) == 1:
            return 'toggle'  # 单个按钮 Toggle
    
    return None
```

---

## 与其他 Skills 的协作

```
[1] figma-ios-component-state-recognition
    ↓ 识别选中/未选中视觉特征 → updateAppearance() + isSelected 模型字段

[2] figma-ios-listview-recognition
    ↓ 判定 UICollectionView（禁 UITableView）→ audit / _layout_hint.list

[2b] figma-ios-uicollectionview-codegen（list 命中后必须加载）
    ↓ CV / Cell / DataSource 骨架 + Mock

[3] figma-ios-selection-interaction（本 Skill）
    ↓ 单选/多选/Toggle → didSelectItemAt / toggle 逻辑（不写 TableView）

[4] figma-ios-rxswift-interaction-pattern
    ↓ 按 host.interaction 绑点击（按钮 Toggle 等）
```

**生成顺序：**
1. `figma-ios-component-state-recognition` → 数据模型（含 `isSelected`）+ `updateAppearance`
2. `figma-ios-listview-recognition` → 是否列表
3. **`figma-ios-uicollectionview-codegen`** → CV 骨架（识别为列表时**必做**）
4. **`figma-ios-selection-interaction`（本 Skill）** → Delegate 交互
5. `figma-ios-rxswift-interaction-pattern` → 非列表按钮的点击绑定

---

## 常见错误与避坑指南

### ❌ 错误 1：忘记取消旧选中（单选模式）

**问题：**
```swift
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    // ❌ 错误：直接选中新项，旧项仍然是选中态
    items[indexPath.item].isSelected = true
    collectionView.reloadData()
}
```

**结果：** 多个 Cell 同时选中（违反单选逻辑）

**修复：**
```swift
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    // ✅ 正确：先取消所有，再选中当前
    for i in 0..<items.count {
        items[i].isSelected = false
    }
    items[indexPath.item].isSelected = true
    collectionView.reloadData()
}
```

---

### ❌ 错误 2：忘记刷新 UI

**问题：**
```swift
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    items[indexPath.item].isSelected.toggle()
    // ❌ 错误：没有刷新 UI，数据变了但界面没变
}
```

**结果：** 选中态变化了，但 UI 没有更新

**修复：**
```swift
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    items[indexPath.item].isSelected.toggle()
    // ✅ 正确：刷新 UI
    collectionView.reloadItems(at: [indexPath])
}
```

---

### ❌ 错误 3：单选模式刷新单个 Cell

**问题：**
```swift
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    for i in 0..<items.count {
        items[i].isSelected = false
    }
    items[indexPath.item].isSelected = true
    
    // ❌ 错误：只刷新当前 Cell，旧选中的 Cell 仍显示选中态
    collectionView.reloadItems(at: [indexPath])
}
```

**结果：** 新旧两个 Cell 都显示选中态

**修复：**
```swift
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    for i in 0..<items.count {
        items[i].isSelected = false
    }
    items[indexPath.item].isSelected = true
    
    // ✅ 正确：刷新所有 Cell（或记录旧索引，刷新新旧两个）
    collectionView.reloadData()
}
```

**性能优化版本：**
```swift
private var previousSelectedIndex: Int = 0

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    // 取消旧选中
    items[previousSelectedIndex].isSelected = false
    
    // 选中新项
    items[indexPath.item].isSelected = true
    
    // 只刷新新旧两个 Cell
    collectionView.reloadItems(at: [
        IndexPath(item: previousSelectedIndex, section: 0),
        indexPath
    ])
    
    // 更新记录
    previousSelectedIndex = indexPath.item
}
```

---

### ❌ 错误 4：didSet 与手动调用 updateAppearance 冲突

**问题：**
```swift
private var isSelected: Bool = false {
    didSet {
        updateAppearance()  // didSet 会自动调用
    }
}

func configure(isSelected: Bool) {
    self.isSelected = isSelected
    updateAppearance()  // ❌ 重复调用
}
```

**结果：** `updateAppearance()` 被调用两次

**修复：**
```swift
// 方案 A：只在 didSet 中调用
private var isSelected: Bool = false {
    didSet {
        updateAppearance()
    }
}

func configure(isSelected: Bool) {
    self.isSelected = isSelected  // ✅ 自动触发 didSet
}
```

**或者：**
```swift
// 方案 B：不用 didSet，手动调用
private var isSelected: Bool = false

func configure(isSelected: Bool) {
    self.isSelected = isSelected
    updateAppearance()  // ✅ 手动调用
}
```

---

### ❌ 错误 5：多选模式下不允许全部取消

**问题：**
```swift
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    items[indexPath.item].isSelected.toggle()
    
    // ❌ 业务需求：至少选中一个，但没有验证
    collectionView.reloadItems(at: [indexPath])
}
```

**结果：** 用户可以取消所有选中，违反业务规则

**修复：**
```swift
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let currentItem = items[indexPath.item]
    
    // 如果当前是选中态，且是唯一选中项，不允许取消
    if currentItem.isSelected {
        let selectedCount = items.filter { $0.isSelected }.count
        if selectedCount == 1 {
            // TODO(阶段3): 结合 PRD 决定交互（Toast 提示 / 禁用按钮 / 忽略）
            print("至少需要选中一个标签")
            return
        }
    }
    
    // Toggle 选中态
    items[indexPath.item].isSelected.toggle()
    collectionView.reloadItems(at: [indexPath])
}
```

---

## 自动化检查清单

生成代码后，必须验证：

```
✅ 数据模型包含 isSelected 字段
✅ 默认选中项正确初始化（isSelected = true）
✅ 单选模式：点击时先取消所有，再选中当前
✅ 多选模式：点击时 Toggle 当前项
✅ UI 刷新逻辑正确（reloadData / reloadItems）
✅ RxSwift 使用 [weak self]（避免循环引用）
✅ 留 TODO 注释提示业务逻辑
✅ didSet 与手动调用不冲突
✅ 多选模式考虑业务约束（如至少选中一个）
```

---

## 代码生成规范

### 1. 注释规范

```swift
// ========== 单选模式：取消所有选中，只选中当前项 ==========
// 或
// ========== 多选模式：Toggle 当前项 ==========
// 或
// ========== Toggle 模式：切换选中态 ==========
```

### 2. TODO 注释规范

```swift
// TODO(阶段3): 结合 PRD + 接口文档补业务逻辑（网络请求 / 页面跳转 / 数据联动等）
// 示例：
// - 网络请求：根据选中品类加载数据
// - 页面跳转：跳转到详情页
// - 数据联动：影响其他 UI 组件
```

### 3. 调试输出规范

```swift
print("选中品类: \(categories[indexPath.item].title)")
// 或
let selectedTags = tagItems.filter { $0.isSelected }.map { $0.title }
print("已选中标签: \(selectedTags)")
```

---

## 相关 Skill

- **总入口**：[figma-ios-playbook](../figma-ios-playbook/SKILL.md)
- **状态识别**：[figma-ios-component-state-recognition](../figma-ios-component-state-recognition/SKILL.md)
- **ListView 识别**：[figma-ios-listview-recognition](../figma-ios-listview-recognition/SKILL.md)
- **交互事件**：[figma-ios-rxswift-interaction-pattern](../figma-ios-rxswift-interaction-pattern/SKILL.md)
- **标准组件**：[figma-ios-standard-components](../figma-ios-standard-components/SKILL.md)

---

**生成时间：** 2026-03-31  
**核心价值：** 自动生成选中态交互逻辑，减少重复代码，统一交互模式  
**适用范围：** 所有涉及选择交互的 UI 组件（ListView、按钮组、Toggle 按钮等）

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [mythkiven](https://github.com/mythkiven)
- **Source:** [mythkiven/figma-ios-codegen](https://github.com/mythkiven/figma-ios-codegen)
- **License:** MIT
- **Homepage:** https://github.com/mythkiven/figma-ios-codegen/tree/main/docs

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-mythkiven-figma-ios-codegen-figma-ios-selection-interaction
- Seller: https://agentstack.voostack.com/s/mythkiven
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
