From 5e0560d75acb73cb133439f4145184507d832531 Mon Sep 17 00:00:00 2001 From: yuqucheng <> Date: Tue, 7 Jul 2026 10:43:07 +0800 Subject: [PATCH] feat(editor): add iOS icon auto-replace post processor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 解决Unity打包iOS时压缩图标导致模糊的问题,将Assets/Icons/iOS/下的高清图标自动替换到Xcode项目的Assets.car中 --- Assets/Editor/iOSIconPostProcessor.cs | 100 ++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 Assets/Editor/iOSIconPostProcessor.cs diff --git a/Assets/Editor/iOSIconPostProcessor.cs b/Assets/Editor/iOSIconPostProcessor.cs new file mode 100644 index 0000000..0803d6a --- /dev/null +++ b/Assets/Editor/iOSIconPostProcessor.cs @@ -0,0 +1,100 @@ +#if UNITY_IOS +using UnityEditor; +using UnityEditor.Callbacks; +using System.IO; +using System.Text; + +/// +/// iOS 出包后自动替换高清 App 图标,解决 Unity 压缩导致图标模糊的问题。 +/// 使用方式:将精确尺寸的 PNG 图标放入 Assets/Icons/iOS/ 目录下,打包时自动生效。 +/// +public class iOSIconPostProcessor +{ + private const string SOURCE_ICON_DIR = "Assets/Icons/iOS/"; + + // 各尺寸图标配置 (idiom, scale, size, 文件名) + private static readonly (string idiom, string scale, string size, string file)[] ICON_CONFIGS = + { + ("iphone", "2x", "20x20", "icon-20@2x.png"), + ("iphone", "3x", "20x20", "icon-20@3x.png"), + ("iphone", "2x", "29x29", "icon-29@2x.png"), + ("iphone", "3x", "29x29", "icon-29@3x.png"), + ("iphone", "2x", "40x40", "icon-40@2x.png"), + ("iphone", "3x", "40x40", "icon-40@3x.png"), + ("iphone", "2x", "60x60", "icon-60@2x.png"), + ("iphone", "3x", "60x60", "icon-60@3x.png"), + ("ipad", "2x", "20x20", "icon-20@2x.png"), + ("ipad", "2x", "29x29", "icon-29@2x.png"), + ("ipad", "2x", "40x40", "icon-40@2x.png"), + ("ipad", "2x", "76x76", "icon-76@2x.png"), + ("ipad", "2x", "83.5x83.5", "icon-83.5@2x.png"), + ("ios-marketing", "1x", "1024x1024", "icon-1024.png"), + }; + + [PostProcessBuild(int.MaxValue)] + public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject) + { + if (target != BuildTarget.iOS) return; + + string iconSetPath = Path.Combine(pathToBuiltProject, "Unity-iPhone/Images.xcassets/AppIcon.appiconset"); + if (!Directory.Exists(iconSetPath)) + { + UnityEngine.Debug.LogWarning($"[iOSIconPostProcessor] 未找到 AppIcon.appiconset 目录: {iconSetPath}"); + return; + } + + int copied = 0; + foreach (var cfg in ICON_CONFIGS) + { + string src = Path.GetFullPath(Path.Combine(SOURCE_ICON_DIR, cfg.file)); + string dst = Path.Combine(iconSetPath, cfg.file); + if (File.Exists(src)) + { + File.Copy(src, dst, overwrite: true); + copied++; + } + else + { + UnityEngine.Debug.LogWarning($"[iOSIconPostProcessor] 缺少图标文件: {src}"); + } + } + + if (copied > 0) + { + WriteContentsJson(iconSetPath); + UnityEngine.Debug.Log($"[iOSIconPostProcessor] 已替换 {copied} 个 App 图标到 Xcode 项目"); + } + } + + private static void WriteContentsJson(string iconSetPath) + { + var sb = new StringBuilder(); + sb.AppendLine("{"); + sb.AppendLine(" \"images\" : ["); + + for (int i = 0; i < ICON_CONFIGS.Length; i++) + { + var cfg = ICON_CONFIGS[i]; + sb.Append(" {"); + sb.Append($"\"idiom\" : \"{cfg.idiom}\", "); + sb.Append($"\"scale\" : \"{cfg.scale}\", "); + sb.Append($"\"size\" : \"{cfg.size}\""); + if (!string.IsNullOrEmpty(cfg.file)) + sb.Append($", \"filename\" : \"{cfg.file}\""); + sb.Append("}"); + if (i < ICON_CONFIGS.Length - 1) sb.Append(","); + sb.AppendLine(); + } + + sb.AppendLine(" ],"); + sb.AppendLine(" \"info\" : {"); + sb.AppendLine(" \"version\" : 1,"); + sb.AppendLine(" \"author\" : \"xcode\""); + sb.AppendLine(" }"); + sb.AppendLine("}"); + + string jsonPath = Path.Combine(iconSetPath, "Contents.json"); + File.WriteAllText(jsonPath, sb.ToString()); + } +} +#endif