Mingyu's Library主页
2026年8月9日

第 42 课(补):生图工具怎么和 Claude Code 配合开发 App?——素材清单 → 生图脚本 → 自动接入 Xcode 资源目录

说明:本文为 2026-10-13 回溯补生成,内容基于当时(D 前后)的公开报道检索。 📅 2026-08-09 · 课型:大纲实战题(课程大纲第 14 课「生图工具怎么和 Claude Code 配合开发 app」) 为什么不是「新功能速用」:本课版本窗口 08-08 至 08-09 内只有 v2.1.226,官方 CHANGELOG 仅写「Bug fixes and reliability improvements」,没有可讲的新功能(来源:CHANGELOG · Havoptic 08 月版本表),因此按规则改讲大纲里待写的实战问题。 前置:第 1 课(CLAUDE.md)、第 5 课(斜杠命令)、第 19 课(Skills)。

场景还原:你在做一个 iOS 小游戏,让 Claude Code 写好了界面,然后去 fal.ai 网页上一张张生成图标和角色图,手动下载、改名、拖进 Xcode。第二天想换一种画风,发现文件名乱七八糟(image (3).png、final_v2_real.png),哪些图在代码里被 Image("...") 引用了也说不清;还有一张图只放了 1x,在真机上发糊。问题不在生图模型,而在生图和代码之间没有一条可重复的流水线。

本课把它拆成四步:先写清单(spec),再用脚本生成,再脚本化导入 Assets.xcassets,最后封装成一个 skill 并加验证。


技巧 1(基础):先写「素材清单 + 命名规范」,让 Claude 按清单干活

解决的问题:素材散落、名字随意,代码引用和实际文件对不上;换画风时不知道要重做哪些图。

Before:「帮我生成几个按钮图标」。After:先让 Claude 读代码,产出一份 assets/manifest.json,之后所有生成和导入都以它为准。

{
  "style": "flat vector, soft pastel palette, thick rounded outlines, plain white background",
  "assets": [
    { "name": "icon_play",     "prompt": "a play button icon",          "size": 180 },
    { "name": "char_cat_idle", "prompt": "a chubby orange cat standing", "size": 300 },
    { "name": "bg_level1",     "prompt": "a meadow with rolling hills",  "size": 1024 }
  ]
}

在 CLAUDE.md 里写规则(第 1 课的老办法):

## 素材规范
- 所有图片素材登记在 assets/manifest.json;name 用 snake_case,前缀 icon_/char_/bg_/ui_
- 代码里只用 Image("<name>") 引用 manifest 中存在的 name;新增素材先改 manifest
- 统一画风写在 manifest.style,单张 prompt 不重复写画风

📖 官方:CLAUDE.md / Memory

技巧 2(进阶):用脚本调 fal.ai 批量生图,而不是在网页上点

解决的问题:网页上一张张点、手动下载,不可重复;换画风要从头再来。

fal.ai 官方 quickstart 的 Python 客户端写法(FAL_KEY 放环境变量,别写进仓库):

pip install fal-client
export FAL_KEY="你的 key"
# scripts/gen_assets.py —— 按 manifest 生成,已存在的跳过(换画风时删掉 raw/ 重跑即可)
import json, os, urllib.request, fal_client
m = json.load(open("assets/manifest.json"))
os.makedirs("assets/raw", exist_ok=True)
for a in m["assets"]:
    out = f"assets/raw/{a['name']}.png"
    if os.path.exists(out):
        continue
    r = fal_client.subscribe("fal-ai/flux/schnell",
                             arguments={"prompt": f"{a['prompt']}, {m['style']}"})
    urllib.request.urlretrieve(r["images"][0]["url"], out)
    print("generated", out)

要点:模型名 fal-ai/flux/schnell 是官方 quickstart 的示例,换成你常用的模型时参数名以该模型页面为准。想用 Gemini(Nano Banana)也行,但它的接口在 2026 年有过迁移(截至 2026-10 官方图片生成文档的示例都改走 Interactions API),照抄前先看当期官方文档里的模型 ID 与请求格式;脚本结构不变,只换「调用 + 取图」那两行。

📖 fal.ai Quickstart · Gemini 图片生成

技巧 3(进阶):脚本化导入 Assets.xcassets,一次生成 1x/2x/3x

解决的问题:手动拖进 Xcode、漏放倍图导致真机发糊;改了图要重新拖。

Xcode 资源目录本质是文件夹 + Contents.json:每个 <name>.imageset 里放图片和一个描述 filename / idiom / scale 的 JSON(格式见 Apple 的 Asset Catalog Format Reference)。所以完全可以让脚本生成。下面用 macOS 自带的 sips 缩放,以 manifest 里的 size 作为 1x 的点尺寸:

# scripts/import_assets.py
import json, os, subprocess
XC = "MyGame/Assets.xcassets"          # 改成你的路径
m = json.load(open("assets/manifest.json"))
for a in m["assets"]:
    d = f"{XC}/{a['name']}.imageset"; os.makedirs(d, exist_ok=True)
    imgs = []
    for s in (1, 2, 3):
        fn = f"{a['name']}@{s}x.png"
        px = a["size"] * s
        subprocess.run(["sips", "-z", str(px), str(px), f"assets/raw/{a['name']}.png",
                        "--out", f"{d}/{fn}"], check=True, capture_output=True)
        imgs.append({"filename": fn, "idiom": "universal", "scale": f"{s}x"})
    json.dump({"images": imgs, "info": {"author": "xcode", "version": 1}},
              open(f"{d}/Contents.json", "w"), indent=2)
    print("imported", a["name"])

注意:生图模型输出分辨率有限,若 size × 3 超过原图尺寸,3x 是被放大的,依然会糊——让脚本在这种情况下打印警告,或在 manifest 里把大图的 size 设小一些;方形缩放只适合方形素材,背景图请按实际宽高比改写 -z 参数。

📖 Apple · Image Set Type(Contents.json 格式)

技巧 4(高阶):封装成 /make-assets skill,并加一道「引用对得上」的验证

解决的问题:每次都要重新交代流程;Claude 写了 Image("icon_pause") 却没生成这张图,运行时才发现空白。

<!-- .claude/skills/make-assets/SKILL.md -->
---
name: make-assets
description: 按 assets/manifest.json 生成并导入 App 图片素材
disable-model-invocation: true
allowed-tools: Read Edit Bash(python3 scripts/*)
---
1. 扫描 Swift 代码里所有 Image("...") 引用,与 assets/manifest.json 对比;缺的条目补进 manifest(prompt 按 $ARGUMENTS 指定的主题写)。
2. 运行 python3 scripts/gen_assets.py,再运行 python3 scripts/import_assets.py。
3. 运行 python3 scripts/check_assets.py,必须输出 OK 才算完成;否则修复后重跑。
# scripts/check_assets.py —— 验证器:代码引用的每个名字都有 imageset
import re, glob, os, sys
XC = "MyGame/Assets.xcassets"
used = set()
for f in glob.glob("MyGame/**/*.swift", recursive=True):
    used |= set(re.findall(r'Image\("([^"]+)"\)', open(f).read()))
have = {os.path.basename(p)[:-9] for p in glob.glob(f"{XC}/**/*.imageset", recursive=True)}
missing = sorted(used - have)
print("MISSING:", missing) if missing else print("OK", len(used), "refs")
sys.exit(1 if missing else 0)

用法:/make-assets 森林主题。验证器是关键:它把「看起来做完了」变成「可检查地做完了」(呼应第 6 课的验证器思路)。

📖 官方:Skills


今日实操(15-20 分钟)

  1. 在你的 iOS 项目里新建 assets/manifest.json,登记 3 张素材(1 个图标、1 个角色、1 个背景),并把技巧 1 的规则写进 CLAUDE.md。
  2. 让 Claude Code 写出技巧 2-4 的三个脚本(路径改成你的工程),跑一遍。
  3. 在任意 SwiftUI 视图里加一行 Image("ui_not_exist"),运行 python3 scripts/check_assets.py。

验收标准: - Assets.xcassets 里出现 3 个 .imageset,每个含 @1x/@2x/@3x 三张图和合法的 Contents.json,Xcode 打开无黄色警告; - 第 3 步验证器输出 MISSING: ['ui_not_exist'] 并以非 0 退出;删掉那行后输出 OK; - 把 manifest 的 style 改成另一种画风、删掉 assets/raw/ 后一条 /make-assets 就能整体换肤。