#if UNITY_EDITOR using System.IO; using UnityEditor; using UnityEditor.Callbacks; using UnityEngine; #if UNITY_IOS using UnityEditor.iOS.Xcode; #endif /// /// 在 tysdk (PostProcessBuild 1000) 之后执行, /// 将备选 App 图标打入 iOS 包并写入 Info.plist,供 App Store 产品页优化(图标 A/B)使用。 /// public static class IOSBuildProcessor { const int PostProcessOrder = 1001; /// 与 App Store Connect 中「备选图标」标识符保持一致(建议仅字母数字)。 public const string AlternateIconIdentifier = "FishingTravelAlt"; static readonly string SourceIconProjectPath = "Assets/app_icons/icon_fishing_travel_alt.png"; #if UNITY_IOS const string DestRelativeFolder = "AlternateAppIcons"; static readonly (string fileName, int width, int height)[] IconOutputs = { ("FishingTravelAlt60x60@2x.png", 120, 120), ("FishingTravelAlt60x60@3x.png", 180, 180), ("FishingTravelAlt76x76@2x~ipad.png", 152, 152), ("FishingTravelAlt83.5x83.5@2x~ipad.png", 167, 167), }; #endif [PostProcessBuild(PostProcessOrder)] public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject) { #if UNITY_IOS if (target != BuildTarget.iOS) return; string srcAbs = Path.Combine(Application.dataPath, "app_icons/icon_fishing_travel_alt.png"); if (!File.Exists(srcAbs)) { Debug.LogError( $"[IOSBuildProcessor] 未找到备选图标: {srcAbs}(项目内 {SourceIconProjectPath})。"); return; } byte[] pngBytes = File.ReadAllBytes(srcAbs); var srcTex = new Texture2D(2, 2, TextureFormat.RGBA32, false); if (!srcTex.LoadImage(pngBytes)) { Debug.LogError("[IOSBuildProcessor] 无法解码 PNG: " + srcAbs); Object.DestroyImmediate(srcTex); return; } string destDir = Path.Combine(pathToBuiltProject, DestRelativeFolder); Directory.CreateDirectory(destDir); foreach (var entry in IconOutputs) { Texture2D scaled = ScaleTexture(srcTex, entry.width, entry.height); try { File.WriteAllBytes(Path.Combine(destDir, entry.fileName), scaled.EncodeToPNG()); } finally { Object.DestroyImmediate(scaled); } } Object.DestroyImmediate(srcTex); AddIconsToXcodeProject(pathToBuiltProject); MergeAlternateIconsPlist(pathToBuiltProject); #else if (target == BuildTarget.iOS) Debug.LogWarning("[IOSBuildProcessor] 当前编辑器未启用 iOS 模块,已跳过备选图标处理。"); #endif } #if UNITY_IOS static Texture2D ScaleTexture(Texture2D source, int width, int height) { var rt = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); var prev = RenderTexture.active; Graphics.Blit(source, rt); RenderTexture.active = rt; var dest = new Texture2D(width, height, TextureFormat.RGBA32, false); dest.ReadPixels(new Rect(0, 0, width, height), 0, 0); dest.Apply(); RenderTexture.active = prev; RenderTexture.ReleaseTemporary(rt); return dest; } static void AddIconsToXcodeProject(string pathToBuiltProject) { string pbxPath = PBXProject.GetPBXProjectPath(pathToBuiltProject); var proj = new PBXProject(); proj.ReadFromFile(pbxPath); string mainTarget = proj.GetUnityMainTargetGuid(); foreach (var entry in IconOutputs) { string relPath = Path.Combine(DestRelativeFolder, entry.fileName).Replace('\\', '/'); string guid = proj.AddFile(relPath, relPath, PBXSourceTree.Source); proj.AddFileToBuild(mainTarget, guid); } proj.WriteToFile(pbxPath); } static void MergeAlternateIconsPlist(string pathToBuiltProject) { string plistPath = Path.Combine(pathToBuiltProject, "Info.plist"); var plist = new PlistDocument(); plist.ReadFromString(File.ReadAllText(plistPath)); PlistElementDict root = plist.root; root.SetBoolean("UIApplicationSupportsAlternateIcons", true); PlistElementDict bundleIcons; if (!root.values.ContainsKey("CFBundleIcons")) bundleIcons = root.CreateDict("CFBundleIcons"); else bundleIcons = root.values["CFBundleIcons"].AsDict(); PlistElementDict alternateIcons; if (!bundleIcons.values.ContainsKey("CFBundleAlternateIcons")) alternateIcons = bundleIcons.CreateDict("CFBundleAlternateIcons"); else alternateIcons = bundleIcons.values["CFBundleAlternateIcons"].AsDict(); if (alternateIcons.values.ContainsKey(AlternateIconIdentifier)) alternateIcons.values.Remove(AlternateIconIdentifier); PlistElementDict iconDict = alternateIcons.CreateDict(AlternateIconIdentifier); PlistElementArray files = iconDict.CreateArray("CFBundleIconFiles"); files.AddString("FishingTravelAlt60x60"); files.AddString("FishingTravelAlt76x76"); files.AddString("FishingTravelAlt83.5x83.5"); iconDict.SetBoolean("UIPrerenderedIcon", false); File.WriteAllText(plistPath, plist.WriteToString()); } #endif } #endif