iOS App圖標版本化

絕大部分 App 都會有測試版、AppStore 正式版,通常情況下,我們不能很快速的確定使用者安裝 App 的環(huán)境,版本號,某個分支,某次提交的代碼,這樣一來,對測試和開發(fā)都造成一定的困惑,定位問題不夠高效。

我們可以通過將重要信息,添加到 App 圖標上,來提高測試環(huán)境定位問題的效率,這里簡稱:iOS 圖標版本化??。

iOS圖標版本化

一、如何獲取需要覆蓋圖標的信息?

  • App版本號
  • 構(gòu)建版本號
  • 分支名
  • 提交哈希值

在 App 的 plist 文件中,可以通過 PlistBuddy 工具,直接提取相關(guān)信息。(根據(jù) Xcode 中 plist 對應(yīng)的 key )
Git 命令行工具提供了 rev-parse 命令,Git 探測工具,獲取 Git 信息。

  1. 獲取 App 版本號:
version=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${CONFIGURATION_BUILD_DIR}/${INFOPLIST_PATH}"`
  1. 獲取構(gòu)建版本號:
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`

二、如何將關(guān)鍵信息覆蓋到App圖標?

ImageMagic 是我用來從命令行處理圖像的工具,它提供了大量的功能。

首先確保安裝 imageMagickghostScript ,可以使用 brew 來簡化安裝過程:

  1. 安裝 imageMagick
brew install imagemagick
安裝imageMagick
  1. 安裝 ghostScript
brew install ghostscript
安裝ghostScript
  1. 我們可以使用 convert 函數(shù),通過指定參數(shù),imageMagick 會把文本覆蓋在圖片上面,還可以設(shè)置底部對齊和默認高度。

imageMagick (TM) 是一個免費的創(chuàng)建、編輯、合成圖片的軟件。
它可以讀取、轉(zhuǎn)換、寫入多種格式的圖片。
圖片切割、顏色替換、各種效果的應(yīng)用,圖片的旋轉(zhuǎn)、組合,文本,直線,多邊形,橢圓,曲線,附加到圖片伸展旋轉(zhuǎn)。

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-構(gòu)建版本號

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 構(gòu)建時,執(zhí)行 icon_version.sh 腳本

Build Phases中,選擇 New Run Script Phase 添加 Run Script。

Shell 內(nèi)容填寫"${SRCROOT}/DaRenShop/Other/Release/icon_version.sh"。

?注意:
${SRCROOT}/自己工程實際的文件路徑/icon_version.sh

4. 配置圖標 AppIcon 資源文件。

注意:
按照實際生成AppIcon資源文件名修改

Build Settings 中,選擇 Asset Catalog App Icon Set Name

  • Debug 設(shè)置為 AppIcon-Internal
  • Release設(shè)置為AppIcon。

注意:確保 AppIcon-Internal 已經(jīng)生成,再配置 Asset Catalog App Icon Set Name

5.配置提交 Git 忽略文件

.gitignore 中添加 AppIcon-Internal.appiconset/ ,提交 Git 時忽略生成的App圖標資源文件。

6. 運行 Xcode 工程

自動生成一套,名為 AppIcon-Internal ,含有覆蓋信息的 App 圖標資源文件。??????

最終App圖標

四、總結(jié)

關(guān)于 Xcode9 構(gòu)建 iOS11 系統(tǒng)的 App 圖標時,不顯示的問題:

使用 Xcode9 構(gòu)建 iOS11 系統(tǒng)的 App 圖標,默認讀取資源文件,而非 App 包的 Icon 圖標,導(dǎo)致不顯示,使用

本文中,通過生成獨立的 AppIcon-Internal 資源文件:

  • 不區(qū)分 Release 和 Debug 構(gòu)建,都會生成 AppIcon-Internal 資源圖標文件。
  • 不區(qū)分 Xcode 版本,需要手動設(shè)置正式版、測試版的 App Icons Source。

另一種,通過 AppIcon 資源文件在 App 包中生成圖標:

  • 區(qū)分 Release 和 Debug 構(gòu)建,不會生成 AppIcon-Internal 資源圖標文件,只在 Debug 下自動替換 App 原圖標。
  • 需要使用 Xcode8 構(gòu)建,不需要手動設(shè)置正式版、測試版的 App Icons Source。
  • Xcode9 構(gòu)建 iOS11 系統(tǒng)圖標時,會不顯示。

腳本傳送門

為了兼容 iOS11系統(tǒng),本文中,通過生成獨立的 AppIcon-Internal 資源文件的原因:

Xcode管理app的icon,通過asset資源目錄。
Xcode還包含app的icon文件和Info.plist,是為了向后兼容。
該腳本替換了app根目錄中的文件,而不是asset資源目錄,在iOS11中使用asset資源目錄,而不是app根目錄中的文件。

我們做的是,在我們的項目中添加一個新的運行腳本,來執(zhí)行以下操作:
1.對AppIcon.appiconset文件的每一圖標。
2.添加模糊效果,版本信息,提交信息。
3.將處理好的圖標,復(fù)制到AppIcon-Internal.appiconset文件。
4.Xcode中配置使用AppIcon-Internal圖標資源文件。
5.刪除無用的構(gòu)建生成的圖標。

基于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 區(qū)分不同渠道版本

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容