iOS App图标版本化

作者: LuisX | 来源:发表于2017-12-13 20:15 被阅读485次

    绝大部分 App 都会有测试版、AppStore 正式版,通常情况下,我们不能很快速的确定使用者安装 App 的环境,版本号,某个分支,某次提交的代码,这样一来,对测试和开发都造成一定的困惑,定位问题不够高效。

    我们可以通过将重要信息,添加到 App 图标上,来提高测试环境定位问题的效率,这里简称:iOS 图标版本化😆。

    iOS图标版本化

    一、如何获取需要覆盖图标的信息?

    • App版本号
    • 构建版本号
    • 分支名
    • 提交哈希值

    在 App 的 plist 文件中,可以通过 PlistBuddy 工具,直接提取相关信息。(根据 Xcode 中 plist 对应的 key )
    Git 命令行工具提供了 rev-parse 命令,Git 探测工具,获取 Git 信息。

    1. 获取 App 版本号:
    version=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}"`
    
    1. 获取构建版本号:
    build_num=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}"`
    
    1. 获取 Git 分支名:
    branch=`git rev-parse --abbrev-ref HEAD`
    
    1. 获取 Git 提交哈希值:
    commit=`git rev-parse --short HEAD`
    

    二、如何将关键信息覆盖到App图标?

    ImageMagic 是我用来从命令行处理图像的工具,它提供了大量的功能。

    首先确保安装 imageMagickghostScript ,可以使用 brew 来简化安装过程:

    1. 安装 imageMagick
    brew install imagemagick
    
    安装imageMagick
    1. 安装 ghostScript
    brew install ghostscript
    
    安装ghostScript
    1. 我们可以使用 convert 函数,通过指定参数,imageMagick 会把文本覆盖在图片上面,还可以设置底部对齐和默认高度。

    imageMagick (TM) 是一个免费的创建、编辑、合成图片的软件。
    它可以读取、转换、写入多种格式的图片。
    图片切割、颜色替换、各种效果的应用,图片的旋转、组合,文本,直线,多边形,椭圆,曲线,附加到图片伸展旋转。

    imageMagick 官方使用文档
    imageMagick 中文站

    imageMagick 处理图片,部分代码片段:

    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
    
    convert "${base_tmp_normalizedFilePath}" /tmp/blurred.png /tmp/mask.png -composite /tmp/temp.png
    

    三、如何快速集成

    1. 配置 icon_version.sh 脚本

    配置下面的代码片段,并保存为 icon_version.sh 脚本文件。

    注意:
    icons_path和icons_dest_path路径,修改为自己工程,实际的图标资源路径或名称。

    #!/bin/sh
    convertPath=`which convert`
    echo ${convertPath}
    if [[ ! -f ${convertPath} || -z ${convertPath} ]]; then
    echo "warning: Skipping Icon versioning, you need to install ImageMagick and ghostscript (fonts) first, you can use brew to simplify process:
    brew install imagemagick
    brew install ghostscript"
    exit -1;
    fi
    
    # 说明
    # commit     git-提交哈希值
    # branch     git-分支名
    # 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}"`
    
    # 检查当前所处Git分支
    if [ -d .git ] || git rev-parse --git-dir > /dev/null 2>&1; then
    commit=`git rev-parse --short HEAD`
    branch=`git rev-parse --abbrev-ref HEAD`
    else
    commit=`hg identify -i`
    branch=`hg identify -b`
    fi;
    
    shopt -s extglob
    build_num="${build_num##*( )}"
    shopt -u extglob
    caption="${version}($build_num)\n${branch}\n${commit}"
    echo $caption
    
    function abspath() { pushd . > /dev/null; if [ -d "$1" ]; then cd "$1"; dirs -l +0; else cd "`dirname \"$1\"`"; cur_dir=`dirs -l +0`; if [ "$cur_dir" == "/" ]; then echo "$cur_dir`basename \"$1\"`"; else echo "$cur_dir/`basename \"$1\"`"; fi; fi; popd > /dev/null; }
    
    function processIcon() {
    base_file=$1
    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 * 50) / 100))
    band_position=$(($height - $band_height))
    text_position=$(($band_position - 8))
    point_size=$(((12 * $width) / 100))
    
    echo "Image dimensions ($width x $height) - band height $band_height @ $band_position - point size $point_size"
    
    #
    # blur band and text
    #
    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
    
    convert "${base_tmp_normalizedFilePath}" /tmp/blurred.png /tmp/mask.png -composite /tmp/temp.png
    
    rm /tmp/blurred.png
    rm /tmp/mask.png
    
    #
    # compose final image
    #
    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}"
    
    # clean up
    rm /tmp/temp.png
    rm /tmp/labels-base.png
    rm /tmp/labels.png
    rm "${base_tmp_normalizedFilePath}"
    
    echo "Overlayed ${final_file_path}"
    }
    
    # Process all app icons and create the corresponding internal icons
    # icons_dir="${SRCROOT}/Images.xcassets/AppIcon.appiconset"
    icons_path="${PROJECT_DIR}/DaRenShop/Images.xcassets/AppIcon.appiconset"
    icons_dest_path="${PROJECT_DIR}/DaRenShop/Images.xcassets/AppIcon-Internal.appiconset"
    icons_set=`basename "${icons_path}"`
    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}"
    
    # Reference: https://askubuntu.com/a/343753
    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
    
    2. 将 icon_version.sh 脚本,添加到项目工程中。
    3. 配置 Xcode 构建时,执行 icon_version.sh 脚本

    Build Phases中,选择 New Run Script Phase 添加 Run Script

    Shell 内容填写"${SRCROOT}/DaRenShop/Other/Release/icon_version.sh"

    注意:
    ${SRCROOT}/自己工程实际的文件路径/icon_version.sh

    4. 配置图标 AppIcon 资源文件。

    注意:
    按照实际生成AppIcon资源文件名修改

    Build Settings 中,选择 Asset Catalog App Icon Set Name

    • Debug 设置为 AppIcon-Internal
    • Release设置为AppIcon

    注意:确保 AppIcon-Internal 已经生成,再配置 Asset Catalog App Icon Set Name

    5.配置提交 Git 忽略文件

    .gitignore 中添加 AppIcon-Internal.appiconset/ ,提交 Git 时忽略生成的App图标资源文件。

    6. 运行 Xcode 工程

    自动生成一套,名为 AppIcon-Internal ,含有覆盖信息的 App 图标资源文件。🎉🎉🎉

    最终App图标

    四、总结

    关于 Xcode9 构建 iOS11 系统的 App 图标时,不显示的问题:

    使用 Xcode9 构建 iOS11 系统的 App 图标,默认读取资源文件,而非 App 包的 Icon 图标,导致不显示,使用

    本文中,通过生成独立的 AppIcon-Internal 资源文件:

    • 不区分 Release 和 Debug 构建,都会生成 AppIcon-Internal 资源图标文件。
    • 不区分 Xcode 版本,需要手动设置正式版、测试版的 App Icons Source。

    另一种,通过 AppIcon 资源文件在 App 包中生成图标:

    • 区分 Release 和 Debug 构建,不会生成 AppIcon-Internal 资源图标文件,只在 Debug 下自动替换 App 原图标。
    • 需要使用 Xcode8 构建,不需要手动设置正式版、测试版的 App Icons Source。
    • Xcode9 构建 iOS11 系统图标时,会不显示。

    脚本传送门

    为了兼容 iOS11系统,本文中,通过生成独立的 AppIcon-Internal 资源文件的原因:

    Xcode管理app的icon,通过asset资源目录。
    Xcode还包含app的icon文件和Info.plist,是为了向后兼容。
    该脚本替换了app根目录中的文件,而不是asset资源目录,在iOS11中使用asset资源目录,而不是app根目录中的文件。
    
    我们做的是,在我们的项目中添加一个新的运行脚本,来执行以下操作:
    1.对AppIcon.appiconset文件的每一图标。
    2.添加模糊效果,版本信息,提交信息。
    3.将处理好的图标,复制到AppIcon-Internal.appiconset文件。
    4.Xcode中配置使用AppIcon-Internal图标资源文件。
    5.删除无用的构建生成的图标。
    
    基于Bootstrap开源项目:
    https://github.com/krzysztofzablocki/Bootstrap
    

    参考脚本:

    WordPress-iOS/Scripts/BuildPhases/AddVersionToIcons.sh
    IconOverlaying/Scripts/iconVersioning.sh

    参考文章:

    iOS——写一个快速定位问题的脚本
    Overlaying application version on top of your icon
    Bootstrap
    iOS App 使用 icon 区分不同渠道版本

    相关文章

      网友评论

      • xxxxxxxx_123:你好,请问一下我在App Icon Source这边只有AppIcon,没有AppIcon-Internal这个选项
        xxxxxxxx_123:@秋儿Luckyfy 请问一下 你在iOS11之后能在appIcon上设置文字么
        秋儿Luckyfy:好像要自己加的
      • stormwyl:build_num=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}"`
        version=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}
        这两行是去包里找plist读取版本信息,通过调整run script位置没有办法使运行run script位置位于process info.plist和generate *.app.dsym两步之间,所以版本信息会落后于plist更新的信息,应该改为去源码目录中找plist信息
      • 5a8c365da0b7:commit、branch能自定义吗?我这样写好像也弄不出那个图片效果?求指教!
        /*
        commit=`111111`
        branch=`dev`
        */
      • 叫我公爵大人:这个我试过之后,Xcode运行后,我自增的build版本号,并不能在当前这次运行看到版本号增加,只能在下一次运行看, 会滞后一次运行,这不能满足日常开发,请问下能解决吗
        5a8c365da0b7:刚测是有这个问题,看来要打包之前先运行一次!

      本文标题: iOS App图标版本化

      本文链接:https://www.haomeiwen.com/subject/savzixtx.html