One keystroke to wrap your Xcode Preview screenshot in an iPhone bezel and copy it to the clipboard.
Press the shortcut you bind in Xcode (e.g. ⌘P) and the tool automatically:
- Grabs a clean preview render (no status bar, Dynamic Island as a black pill), trying two sources in order:
- AppleScript clicks the Xcode menu Editor ▸ Canvas ▸ Copy Preview Screenshot — captures exactly what the Canvas currently shows (including any interaction state), at native 3x resolution
- Fallback: the
RenderPreviewtool of the official Xcode MCP server (xcrun mcpbridge) — rebuilds and renders the initial state of the#Preview; it does not reflect Canvas interaction state and returns a lower resolution
- Detects the transparent screen area of
bezel.pngand composites the screenshot into it with aspect-fill - Saves the result to
.build/last-output.pngand puts it back on the clipboard as PNG + TIFF, ready to paste into Slack, slides, or social media
- Clone this repo
- In Xcode ▸ Settings ▸ Behaviors, add a behavior, check Run, pick
preview-bezel.sh, and bind a shortcut (⌘P recommended) - On first run, allow the Accessibility permission prompt — clicking Xcode's menu requires it; if no prompt appears but the run fails, enable Xcode manually under System Settings ▸ Privacy & Security ▸ Accessibility
- (Only needed for the fallback) Enable Xcode Tools under Xcode ▸ Settings ▸ Intelligence ▸ Model Context Protocol (Xcode 26.3+)
You only hit this on the MCP fallback path: release builds of Xcode show an approval dialog for every newly launched agent connection, with no "always allow" option. The tool handles it in two layers:
- After compiling, the binary is re-signed with your Apple Development certificate (override with
PREVIEW_BEZEL_SIGN_ID) so its signing identity stays stable - At runtime, a background watcher detects the approval dialog and clicks Allow automatically — only after verifying the dialog text contains this tool's own path, so it never dismisses another agent's dialog (requires Accessibility)
- Open a SwiftUI file in Xcode and let the Canvas preview run
- Press your bound shortcut
- When the "copied to clipboard" notification appears, just paste
Replace bezel.png with any device frame image whose screen area is transparent — the tool auto-detects the position and size of the transparent region, no code changes needed.
The bundled bezel.png is the iPhone 17 Pro (Cosmic Orange) product bezel from Apple Design Resources; its screen cutout is exactly 1206×2622, a 1:1 match for preview screenshots. Please follow the Apple Design Resources license terms.
preview-bezel.sh: entry script; recompiles withswiftc(and re-signs with your development certificate) whenever the source changes, then runs the binaryPreviewBezel.swift: core logic- Menu source (primary): AppleScript (System Events) clicks Copy Preview Screenshot and polls the clipboard's
changeCountfor the screenshot — this captures whatever the Canvas currently displays - MCP fallback: a minimal built-in MCP client (JSON-RPC over stdio) connects to
xcrun mcpbridgeand callsXcodeListWindows→XcodeGetCurrentFile(find the .swift file being edited) →RenderPreview(render the file's first#Previewin its initial state), then reads the returnedpreviewSnapshotPath; since Xcode prompts for approval on every agent connection, a background watcher auto-clicks Allow (strictly matched against this tool's own path) - Reads the bezel's alpha channel and walks outward from the center to find the transparent screen rectangle (vertical walks use columns at 18%–82% of the screen width to avoid the Dynamic Island)
- Flood-fills from the image borders to find the region outside the phone silhouette and erases aspect-fill overflow (the screen has rounded corners, so the bounding rectangle's corners stick out past the silhouette)
- Finally draws the bezel on top, writes the PNG, and fills the clipboard
- Menu source (primary): AppleScript (System Events) clicks Copy Preview Screenshot and polls the clipboard's
Note: the Editor menu contains two items named "Canvas" (a visibility toggle and a submenu), and AppleScript rewrites index references stored in variables into name references, so all menu access uses inline index chains — see the comments in
PreviewBezel.swift.
一鍵把 Xcode Preview 截圖套上 iPhone bezel 外框,並複製到剪貼簿。
按下 Xcode 裡綁定的快捷鍵(例如 ⌘P),就會自動:
- 取得乾淨的 preview 渲染圖(無狀態列、Dynamic Island 為黑色藥丸),依序嘗試兩種來源:
- AppleScript 點擊 Xcode 選單 Editor ▸ Canvas ▸ Copy Preview Screenshot——擷取 Canvas 目前顯示的畫面(含互動後的狀態),原生 3x 解析度
- 備援:Xcode 官方 MCP server(
xcrun mcpbridge)的RenderPreview工具——重新建置並渲染#Preview的初始狀態,不反映 Canvas 互動現況、解析度較低
- 自動偵測
bezel.png的透明螢幕區域,把截圖以 aspect-fill 合成進去 - 合成結果存到
.build/last-output.png,同時以 PNG + TIFF 放回剪貼簿,直接貼到 Slack、簡報或社群
- Clone 這個 repo
- 在 Xcode ▸ Settings ▸ Behaviors 新增一個 behavior,勾選 Run,選擇
preview-bezel.sh,並綁定快捷鍵(建議 ⌘P) - 第一次執行時,若系統詢問「輔助使用」(Accessibility)權限,請允許——點擊 Xcode 選單需要這個權限;若沒有跳出詢問但執行失敗,到「系統設定 ▸ 隱私權與安全性 ▸ 輔助使用」手動打開
- (備援方案才需要)在 Xcode ▸ Settings ▸ Intelligence ▸ Model Context Protocol 啟用 Xcode Tools(Xcode 26.3+)
只有走 MCP 備援時才會遇到:正式版 Xcode 對每次新啟動的 agent 連線都會跳授權視窗,沒有「永久允許」機制。本工具做了兩層處理:
- 編譯後自動用 Apple Development 憑證重簽 binary(可用
PREVIEW_BEZEL_SIGN_ID指定憑證),讓簽章身分穩定 - 執行時背景偵測授權視窗,確認內容包含本工具路徑後自動點擊 Allow(只點自己的,不會誤點其他 agent 的授權視窗;需要輔助使用權限)
- 在 Xcode 打開 SwiftUI 檔案,讓 Canvas preview 跑起來
- 按下綁定的快捷鍵
- 收到「已合成並複製到剪貼簿」通知後直接貼上即可
把 bezel.png 換成任何「螢幕區域為透明」的裝置外框圖即可,程式會自動偵測透明區域的位置與大小,不需要改程式碼。
目前附的 bezel.png 是 iPhone 17 Pro(宇宙橙),來自 Apple Design Resources 的產品外框素材,螢幕挖洞剛好是 1206×2622,與 preview 截圖 1:1。使用時請遵守 Apple Design Resources 的授權條款。
preview-bezel.sh:入口腳本,原始碼有更新時自動用swiftc重新編譯(並用開發憑證重簽),再執行 binaryPreviewBezel.swift:核心邏輯- 選單來源(主要):用 AppleScript(System Events)點擊 Copy Preview Screenshot,輪詢剪貼簿
changeCount取得截圖——擷取的是 Canvas 當下顯示的畫面 - MCP 備援:內建一個極簡 MCP client(JSON-RPC over stdio),連上
xcrun mcpbridge後依序呼叫XcodeListWindows→XcodeGetCurrentFile(取得目前編輯中的 .swift 檔)→RenderPreview(渲染該檔第一個#Preview的初始狀態),讀取回傳的previewSnapshotPath;Xcode 對每次 agent 連線都會跳授權視窗,工具會背景偵測並自動點 Allow(嚴格比對視窗內容包含本工具路徑,不會誤點其他 agent 的) - 讀取 bezel 圖的 alpha channel,從中心往四周走出螢幕的透明矩形(垂直方向取螢幕寬度 18%–82% 的直欄,避開 Dynamic Island)
- 從影像邊界 flood fill 找出手機輪廓以外的區域,清除 aspect-fill 溢出的像素(螢幕是圓角,外接矩形的四角會超出輪廓)
- 最後把 bezel 疊在最上層,輸出 PNG 並寫入剪貼簿
- 選單來源(主要):用 AppleScript(System Events)點擊 Copy Preview Screenshot,輪詢剪貼簿
註:Editor 選單有兩個同名的「Canvas」項目(顯示開關與子選單),而且 AppleScript 會把存進變數的索引引用改寫成名稱引用,所以選單存取全部使用行內索引鏈,細節見
PreviewBezel.swift的註解。
