请注意,该篇博客内容为我用ai让它帮我总结开发过程生成的,但是实测是好用的,可以看我的实际效果:
最右下角,回收站左面的就是我的app的图标,可以看到大小比其他我在网上下载的别人的app图标小了,也加上了圆角,和系统图标一致!
如果你不满意,你可以更改我下面的py脚本中的其他参数,调整图标的透明遮罩和大小,重新编译项目应用,调整到你满意为止。
如果你不想亲自做这些事,我准备了一个skill,你可以把任务给你的agent:
通过网盘分享的文件:macos-app-icon.skill
链接: https://pan.baidu.com/s/11_EsMQ5zTVDSgORGcUYhlQ?pwd=2qga 提取码: 2qga

代码一:生成图标(make_icon.py)
pythonÍ
from PIL import Image, ImageDraw
import os
# ================= 配置区 =================
SRC = "source.png" # 原始图标,任意尺寸,建议 ≥1024px
CORNER = 0.18 # 圆角曲率 18%
PADDING_RATIO = 0.118 # 边距比例 11.8%(实测与系统图标视觉一致)
# ===========================================
img = Image.open(SRC).convert("RGBA")
side = min(img.size)
img = img.crop((0, 0, side, side)) # 裁切为正方形
# ① 圆角遮罩
mask = Image.new("L", (side, side), 0)
ImageDraw.Draw(mask).rounded_rectangle(
(0, 0, side, side),
radius=int(side * CORNER), # 圆角半径 = 边长 × 18%
fill=255,
)
img.putalpha(mask) # 应用圆角
# ② 加 padding
PADDING = int(side * PADDING_RATIO)
canvas = Image.new(
"RGBA",
(side + PADDING * 2, side + PADDING * 2),
(0, 0, 0, 0), # 透明背景
)
canvas.paste(img, (PADDING, PADDING), img) # 居中贴图
# ③ 生成全部 10 个 iconset 尺寸
# 关键:全程 Pillow Lanczos,绝不用 sips(会破坏 alpha)
final = canvas.resize((1024, 1024), Image.Resampling.LANCZOS)
os.makedirs("MyIcon.iconset", exist_ok=True)
sizes = {
"icon_16x16.png": 16, "icon_16x16@2x.png": 32,
"icon_32x32.png": 32, "icon_32x32@2x.png": 64,
"icon_128x128.png": 128, "icon_128x128@2x.png": 256,
"icon_256x256.png": 256, "icon_256x256@2x.png": 512,
"icon_512x512.png": 512, "icon_512x512@2x.png": 1024,
}
for name, size in sizes.items():
final.resize((size, size), Image.Resampling.LANCZOS).save(
f"MyIcon.iconset/{name}"
)
# ④ 打包 .icns
os.system("iconutil -c icns MyIcon.iconset -o icon.icns")
print("Done: icon.icns")
运行:
bash
pip install Pillow
python3 make_icon.py
代码要点:
- ①② 是核心:圆角和 padding 直接做进源图,而不是指望系统
- ③ 别用 sips :
sips -z缩放带 alpha 的 PNG 会产生半透明插值,严重时整个图标变透明;PillowLANCZOS对 RGBA 处理正确 - ④ 尺寸一个不能错:iconset 缺标准尺寸时 macOS 会拿大图缩放顶替,Dock 里显得偏大
代码二:py2app 指定图标(setup.py)
python
from setuptools import setup
APP = ["main.py"]
DATA_FILES = []
OPTIONS = {
"iconfile": "icon.icns", # ← 关键行,指定图标
"argv_emulation": False,
"plist": {
"CFBundleName": "Your App",
"CFBundleIdentifier": "com.your.app",
"CFBundleVersion": "1.0.0",
},
}
setup(
app=APP,
data_files=DATA_FILES,
options={"py2app": OPTIONS},
setup_requires=["py2app"],
)
bash
python3 setup.py py2app
代码三:清理 + 签名(build.sh)
bash
#!/bin/bash
set -e
APP="dist/Your App.app"
# ① 删除 Tcl/Tk 框架里 codesign 无法处理的静态库
find "$APP/Contents/Frameworks" -name "*.a" -delete
# ② 清除扩展属性
xattr -cr "$APP"
# ③ ad-hoc 签名
codesign --force --deep --sign - "$APP"
# ④ 验证
codesign --verify --verbose "$APP"
要点:py2app 的内建签名遇到 Tcl/Tk 框架的 .a 文件会直接抛 RuntimeError: Cannot sign bundle,必须先删再手动签。未签名的 app 在 Apple Silicon 上会以 Code Signature Invalid 崩溃。
原理速览
| 现象 | 机制 | 解法 |
|---|---|---|
| 图标没圆角 | Dock 自动加 squircle,但切在满铺内容边缘看不出来 | 源图自带 padding,露出圆角 |
| 图标比系统大 | 系统应用走 Asset Catalog(Assets.car)带自动 margin,纯 .icns 无此待遇 |
源图自带 padding 补偿 |
| 图标全透明 | sips 缩放破坏 alpha 通道 |
全程 Pillow |
.icns 本质是 10 张静态 PNG 的容器,Dock 原样渲染不做加工。所以一切视觉效果(圆角、留白)都必须在生成阶段做进图里,不能指望运行时。