先UI预览后编码(TinyCorn Studio 标准)
规则: 所有界面更改必须在应用于代码之前,先提供预览图片供用户批准。
工作流程
-
编写离线渲染脚本 在
tools/preview_<名称>.swift中——无需运行应用,直接渲染为 2x PNG。 -
运行 并打开图片供用户查看:
swift tools/preview_<名称>.swift ~/Desktop/<App>-<名称>-预览.png open ~/Desktop/<App>-<名称>-预览.png -
请求批准 —— 如果有多种设计方案,并排渲染 2–4 个方案,请用户选择(A/B/C/D)。
-
仅在用户同意后应用。 将预览脚本随功能一起提交。
-
如果用户要求修改:编辑脚本,重新渲染,重复此过程直到获得批准。
代码骨架(SwiftUI, macOS)
import AppKit
import SwiftUI
struct PreviewRoot: View {
var body: some View {
// 使用应用的组件/颜色构建模拟 UI
VStack { /* ... */ }
.frame(width: 900, height: 600)
.background(Color(nsColor: .windowBackgroundColor))
}
}
let host = NSHostingView(rootView: PreviewRoot())
host.frame = NSRect(x: 0, y: 0, width: 900, height: 600)
host.layoutSubtreeIfNeeded()
let rep = host.bitmapImageRepForCachingDisplay(in: host.bounds)!
host.cacheDisplay(in: host.bounds, to: rep)
let png = rep.representation(using: .png, properties: [:])!
let out = CommandLine.arguments.count > 1
? CommandLine.arguments[1]
: "preview.png"
try! png.write(to: URL(fileURLWithPath: out))
print("已写入 \(out)")
常见陷阱(实际遇到过)
- 不要使用与 SwiftUI 修饰符/视图同名的全局变量(例如
border)→ 会导致重载错误。请使用borderColor。 - 多个方法之间共享头文件,因此 不要在同一个文件的其他地方重复声明。
- 2x 渲染:设置
rep.size或使用如上所示的bitmapImageRepForCachingDisplay+cacheDisplay;在缓存前不要忘记layoutSubtreeIfNeeded()。 - 不要使用
screencapture/AppleScript 来截取应用截图——机器通常缺少屏幕录制权限(错误码-1743)。离线渲染是唯一可靠的方法。 - 应用使用暗色主题:使用相同的
Color(nsColor:)和应用的资源进行预览,以确保与实际效果匹配。 - 将图片保存到
~/Desktop/以便用户查找,文件名格式为<App>-<功能>-预览.png。
非 SwiftUI 情况
- Web:使用浏览器打开 HTML 模型图并截图,或使用 Playwright 截图。
- CLI/TUI:在终端中使用 ANSI 渲染,或使用带颜色的文本描述。
不变的基本原则:先有图片,后有代码,用户批准后才应用。