iOS - Run Script之修改App icon

Run Script 之 修改AppIcon

Created by Ningyuan 2020/04/26

需求背景

方便地从图标上区分应用的版本以及环境等信息。

准备

首先,需要在Mac安装Homebrew,如果还没安装,请访问https://brew.sh了解并自行安装。

本次用到的主要工具为ImageMagick,它可以提供对图像的多种处理,有兴趣的可以自行学习。链接如下:

ImageMagick官方网址:https://www.imagemagick.org/script/command-line-options.php

ImageMagick中文站:http://www.imagemagick.com.cn

打开终端并输入以下命令,安装ImageMagick、GhostScripts:

brew install imagemagick

brew install ghostscript

简单尝试

  1. 下面使用convert命令,为”icon-60@2x.png“这张图片附加字体、图片,可自行找图片测试。
convert icon-60@2x.png -fill black -font Times-Bold -pointsize 18 -gravity south -annotate 0 "Hello World" test.png
```shell
# (运行项目后)应用所在路径,待会要用来查看生成的图标
echo "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"

# 项目所在文件夹路径
echo "${SRCROOT}"
```
  1. 然后,Xcode -> Targets -> Build Phases -> 选“+” -> New Run Script phase -> 在新建的Run Script中填上下面的测试脚本代码,修改BASE_IMAGE_PATHnew.png为自己的资源即可:

    BASE_IMAGE_PATH:要转换的图片路径,- name 后的AppIcon60x60@2x.png修改为项目中的其中一张图标名称

    new.png:beta图标的图片名称,同样需要修改为自己的图标

    # Type a script or drag a script file from your workspace to insert its path.
    
    echo "hello sh"
    echo "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
    
    IFS=$'\n'
    
    PATH=${PATH}:/usr/local/bin
    
    IFS=$'\n'
    
    function generateIcon () {
    
    BASE_IMAGE_NAME=$1
    
    #获取本地的应用图标,然后分别将该路径保存到TARGET_PATH 和 BASE_IMAGE_PATH 变量中
    TARGET_PATH="${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/${BASE_IMAGE_NAME}"
    
    BASE_IMAGE_PATH=$(find ${SRCROOT} -name "AppIcon60x60@2x.png")
    
    WIDTH=$(identify -format %w ${BASE_IMAGE_PATH})
    
    FONT_SIZE=$(echo "$WIDTH * .15" | bc -l)
    
    #版本号
    versionNumber=$MARKETING_VERSION
    #构建号
    buildNumber=$CURRENT_PROJECT_VERSION
    
    #将new.png的图标的尺寸改为合适的大小
    convert new.png -resize $WIDTHx$WIDTH resizedNew.png
    
    #首先,它在原始的应用图标上添加"Hello World"文本。
    #然后该脚本执行合成的功能--将刚刚新生成的resizedNew.png放置在其上面。然后将合成的图片保存为应用的图标。
    convert ${BASE_IMAGE_PATH} -fill black -font Times-Bold -pointsize ${FONT_SIZE} -gravity south -annotate 0 "$buildNumber" - | composite resizedNew.png - ${TARGET_PATH}
    
    }
    
    generateIcon "AppIcon60x60@2x.png"
    generateIcon "AppIcon60x60@3x.png"
    
    generateIcon "AppIcon76x76~ipad.png"
    generateIcon "AppIcon76x76@2x~ipad.png"
    
    generateIcon "AppIcon83.5x83.5@2x~ipad.png"
    
  2. Command + B编译,顺利的话会生成相应的图标,进入下面显示的路径,则可看到刚刚生成的图片:AppIcon60x60@2x.png、AppIcon60x60@3x.png、AppIcon76x76ipad.png、AppIcon76x76@2xipad.png、AppIcon83.5x83.5@2x~ipad.png

    echo "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}
    

优化

  1. 实际使用中,将上面的测试脚本修改,封装为一个replaceAppIcon.sh或自己命名的一个.sh文件
  2. 经测试,上述测试脚本只能在iOS10以及之前的版本生效,iOS11以后即使能成功生成图片,但实际运行项目时还是无法替换应用icon,需要将生成的图片存放到Assets.xcassets
  3. 除了Release环境,根据当前scheme中选择的构建环境,自动生成对应.xcassets文件并存放到里面

下面是优化后的代码,复制到新建的.sh文件中,icons_pathicons_desc_path需要根据自己的项目修改成对应路径

#!/bin/sh
# 检查是否已安装所需工具
convertPath=`which convert`
echo ${convertPath}
if [[ ! -f ${convertPath} || -z ${convertPath} ]]; then
echo "监测到未安装ImageMagick、GhostScript,请在终端使用以下命令安装:
brew install imagemagick
brew install ghostscript"
exit -1;
fi

# version    app-版本号
# build_num  app-构建版本号
version=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}"`
build_num=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}"`

caption="${version}\n($build_num)"
echo $caption

# 处理icon
function processIcon() {

    base_file=$1 # icon文件
    temp_path=$2 # 生成文件的临时存放路径
    dest_path=$3 # 目标路径
    # 检查文件、路径是否存在,不存在则退出
    if [[ ! -e $base_file ]]; then
    echo "error: file does not exist: ${base_file}"
    exit -1;
    fi
    if [[ -z $temp_path ]]; then
    echo "error: temp_path does not exist: ${temp_path}"
    exit -1;
    fi
    if [[ -z $dest_path ]]; then
    echo "error: dest_path does not exist: ${dest_path}"
    exit -1;
    fi

    file_name=$(basename "$base_file")
    final_file_path="${dest_path}/${file_name}"
    base_tmp_normalizedFileName="${file_name%.*}-normalized.${file_name##*.}"
    base_tmp_normalizedFilePath="${temp_path}/${base_tmp_normalizedFileName}"
    
    # Normalize
    echo "Reverting optimized PNG to normal"
    echo "xcrun -sdk iphoneos pngcrush -revert-iphone-optimizations -q '${base_file}' '${base_tmp_normalizedFilePath}'"
    xcrun -sdk iphoneos pngcrush -revert-iphone-optimizations -q "${base_file}" "${base_tmp_normalizedFilePath}"
    width=`identify -format %w "${base_tmp_normalizedFilePath}"`
    height=`identify -format %h "${base_tmp_normalizedFilePath}"`
    band_height=$((($height * 40) / 100))
    band_position=$(($height - $band_height))
    text_position=$(($band_position - 4))
    point_size=$(((13 * $width) / 100))
    echo "Image dimensions ($width x $height) - band height $band_height @ $band_position - point size $point_size"
    
    # 添加你自己需定制的beta小标签 如new.png
    baseBetaImg="new.png"
    if [[ $baseBetaImg == "new.png" ]]; then
    echo "不添加小标签。"
    else
        convert ${baseBetaImg} -resize ${width}x${height} /tmp/resizedBetaImg.png
    fi

    # 蒙版、版本号、构建号
    convert "${base_tmp_normalizedFilePath}" -blur 10x8 /tmp/blurred.png
    convert /tmp/blurred.png -gamma 0 -fill white -draw "rectangle 0,$band_position,$width,$height" /tmp/mask.png
    convert -size ${width}x${band_height} xc:none -fill 'rgba(0,0,0,0.2)' -draw "rectangle 0,0,$width,$band_height" /tmp/labels-base.png
    convert -background none -size ${width}x${band_height} -pointsize $point_size -fill white -gravity center -gravity South caption:"$caption" /tmp/labels.png
    
    if [[ $baseBetaImg == "" ]]; then
        convert "${base_tmp_normalizedFilePath}" /tmp/blurred.png /tmp/mask.png -composite /tmp/temp.png
    else
        convert "${base_tmp_normalizedFilePath}" /tmp/blurred.png /tmp/mask.png -composite /tmp/resizedBetaImg.png -composite /tmp/temp.png
        rm /tmp/resizedBetaImg.png
    fi
    rm /tmp/blurred.png
    rm /tmp/mask.png
    
    # 合成最终icon图片
    filename=New"${base_file}"
    convert /tmp/temp.png /tmp/labels-base.png -geometry +0+$band_position -composite /tmp/labels.png -geometry +0+$text_position -geometry +${w}-${h} -composite -alpha remove "${final_file_path}"
    
    # 最后清理临时生成的图片文件
    rm /tmp/temp.png
    rm /tmp/labels-base.png
    rm /tmp/labels.png
    rm "${base_tmp_normalizedFilePath}"
    echo "Overlayed ${final_file_path}"
}

# 获取当前配置环境
config_envir=$CONFIGURATION
if [[$config_envir == "Release"]]; then
echo "Release环境不生成AppIcon"
exit -1;
else
    # 非Release环境才生成新AppIcon
    # Process all app icons and create the corresponding internal icons
    # 本项目源icon路径
    # 组成:@{PROJECT_DIR}/你的项目路径/.xcassets文件(如Assets.xcassets)/AppIcon.appiconset
    icons_path="${PROJECT_DIR}/TingZhiPsychology/Assets.xcassets/AppIcon.appiconset"

    # 生成icon图集存放路径
    icons_dest_path="${PROJECT_DIR}/TingZhiPsychology/Assets.xcassets/AppIcon-${config_envir}.appiconset"

    # 图片临时存放路径
    tmp_path="${TEMP_DIR}/IconVersioning"
    echo "icons_path: ${icons_path}"
    echo "icons_dest_path: ${icons_dest_path}"
    mkdir -p "${tmp_path}"
    if [[ $icons_dest_path == "\\" ]]; then
    echo "error: destination file path can't be the root directory"
    exit -1;
    fi
    rm -rf "${icons_dest_path}"
    cp -rf "${icons_path}" "${icons_dest_path}"

    find "${icons_path}" -type f -name "*.png" -print0 |
    while IFS= read -r -d '' file; do
    echo "$file"
    processIcon "${file}" "${tmp_path}" "${icons_dest_path}"
    done
fi

使用

  1. 新建.sh文件,复制(并修改上述代码AppIcon存放路径)到项目中
  2. 将自己使用的beta图标移动到与.xcodeproj文件同级路径中,同时需要将图标的文件名.后缀填入代码中的baseBetaImg,没有则忽略此步骤
  3. Xcode -> Targets -> Build Phases -> 选“+” -> New Run Script phase -> 在新建的Run Script中填上.sh文件相应的路径,如下图所示:
5ea594264fe6b.png
  1. 先编译一次command + B,然后在Build Setting中搜索AppIcon,填入对应环境生成的AppIcon,如下图所示:
5ea59434adc6f.png
  1. 最后,若已删除新生成的AppIcon,或不需要使用,则需要步骤3中,将不同环境下的AppIcon还原

参考文章

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355