说明:本文为 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 分钟)
- 在你的 iOS 项目里新建
assets/manifest.json,登记 3 张素材(1 个图标、1 个角色、1 个背景),并把技巧 1 的规则写进 CLAUDE.md。 - 让 Claude Code 写出技巧 2-4 的三个脚本(路径改成你的工程),跑一遍。
- 在任意 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 就能整体换肤。
主页