iOS技术积累

不管生活有多不容易,都要守住自己的那一份优雅。

一次关于IconFont的调研

上周在虎哥的直播上承诺放出调研IconFont的经过,周末就坚持者写完这个吧。

调研原因

用户在APP Store下载需要的APP,APP包体积是一个用户比较敏感的数据,过大的APP会对产品的推广产生不良的影响,比如:占用过大的手机存储空间。下载消耗较多网络流量(即便是wifi,后续也可能因为升级过程较为缓慢,导致用户长时间不升级,造成市面上的软件版本碎片化严重)。所以减少APP体积会带来很多正面影响。

掌链现状

我们以掌上链家APP7.7.0举例分析: 现在在Itunes Store下载的IPA体积大约在91.1M,即便公司在iOS9之后新增了APP Slicing功能,在我自己的iPhone 6 Plus下载的APP安装包依然达到了81.7M. 安装包瘦身主要包含资源瘦身和代码段瘦身,这篇文章主要调研资源瘦身。

资源的主要部分就是图片,请看下面数据

我们把iPA解压之后里面的Assert.car资源包的体积大约在34.1M 。 使用软件导出内部的资源文件发现3X系列的图片大小约为18.1M2X系列的图片大小约为19.4M,两份加起来大于34.1M是因为软件导出的图片部分没有后缀名默认两份都会存在。所以可以看出我们的图片还是有很大的优化空间的。

相关数据请看以下图片




IconFont

首先要介绍下矢量图
鉴于矢量图占用尺寸小,放大不失真,我们完全可用一套图来替换iOS传统的2x和3x系列图片,不仅仅是iOS,Android平台也不用切多份图了。

优点

  1. 减小体积,字体文件比图片要小
  2. 图标保真缩放,解决2x/3x乃至将来的nx图问题
  3. 方便更改颜色大小,图片复用

不足

  1. 适用于纯色icon
  2. 需要维护字体库

字体管理

既然设计到字体,我们就要制作字体,制作字体有两种办法,一种是自己手动去做比如使用FontForge工具,另外一种是托管到阿里的iconfont平台,我们的UE做字体设计完全没有意义,可以托管给平台。 托管给平台的好处:

  1. 大大的降低了接入难度
  2. 更方便项目管理。和RD对接更方便

而且iconFont可以和fontForge双重使用,FontForge工具可以再压缩这个字体文件。榨干最后一点剩余空间。

DEMO

iconfont平台建立一个项目,随便去购买几个图标(免费的即可), 06,解压后的文件夹有一个字体文件,双击安装。 mac和windows都有对应的工具管理字体07
近20张icon只占用了10k的资源空间,占用空间极小
08

具体的代码我们这里不再赘述,后面的demo具体展示, 不过我们要讲一讲使用的方案

  1. UILabel作为Icon
  2. UIButton的titleLabel作为Icon
  3. 根据字体生成UIImage

鉴于将我们本来的icon当作字体使用,会让我们在项目中添加的控件方式有所变化,而且不容易控制颜色和图标大小。所以我这里推荐第三种方案。

更详细的方案是我们建立字体组件。将这一块功能单独管理,便于后期迭代和维护,而且屏蔽底层使用直接暴露出对应的图片接口可以让上层无感使用。

后期如何接入

首期工作

这里建议拿掌链下手:原因有一下几点

  1. 掌链面向实际用户,做出来的效果更明显
  2. 掌链的APP安装包也最大,也最有瘦身的价值
  3. 掌链在经过组件化之后资源管理方式较为整齐,后期处理可以分次,灰度过度。 首期可能要麻烦UI同学,整理之前图标的对应的SVG格式,创建对应的字体库文件。工作量可能较大(约千张icon)。组件负责同学建立对应组件。 推荐的模式是可以在IconFont建立项目组,UI为owner,RD为member,方便协作和通知。

后期维护

待稳定后,后续迭代更加简单,只需UI同学更新对应的图标,建立新字体,然后组件管理者可以更新。 对APP安装和UI与RD业务迭代效率都有很大提高。

CODE

文件夹见DEMO

参考

UI参考

http://www.iconfont.cn https://icomoon.io http://fontello.com http://fontawesome.io/icons/

RD参考

Android:https://github.com/mikepenz/Android-Iconics

iOS: https://github.com/PrideChung/FontAwesomeKit

使用IconFont减小iOS应用体积


华丽的分割线


沟通结果

经过沟通发现掌上链家并不太适合这样的方式,原因如下

  1. 之前的icon很多是用ps做的 如果改用iconfont很多图层效果无法实现

  2. 即便新的icon用Sketch制作矢量图,但是icon在掌链中能制作矢量图的占比较小,且对设计的人力要求过大。

  3. 位图(png,jpg)等才是占用资源大户。
    后期考虑的方案

  4. 压缩大图 推荐使用PPDUCK(收费) imageOption,[]tinyPng](https://www.baidu.com/link?url=KkC7lFY89TwZELeYuV93DGrVaUInl3IYeq69FIHm0vp6mG8NrKBif_ewcW-9aZKC&wd=&eqid=cf83ff7b00071ce80000000358d65ecf)

  5. 删除重复资源(上海链家合并期间部分资源重复,但是由于工期太短没有剔除)

  6. 替换旧icon ,且可以统一UI风格

尝试

我尝试使用了上面推荐的三个压缩软件进行压缩,对比如下

  1. imageOption可以使图片减少30%左右,但是压缩巨慢,压缩2500张图片耗费12H以上并且,CPU一直维持在70%左右,但是图片是无损压缩,可以放心使用
  2. PPDuck压缩2500张图片需要花费5分钟不到,效果达到了惊人的70%,但是缺点也很明显,收费
阅读更多

自动检测第三方Pod小工具

需求原因

做了半年的组件化了,原本的项目由一个集中式的仓库开发被拆分为几十个基础组件,还有各种业务组件。仓库在逻辑上分离也给开发和测试带来了很多好处。当然也有不好的地方。业务方的同事对这方面更为敏感,由于开发的时候壳工程有原来的依赖十几个第三方Pod变成了现在依赖将近上百个Pod,频繁的install 或者 update,偶尔会意外造成某些库更新,这些更新可能是不稳定的,而且QA由于不知道这些修改,会导致突然有些bug出现,有时候会造成不必要的沟(Si)通(Bi)。 所以业务方的同学提了一个模糊的需求: 我能不能在每次install 或者update的时候自动检测到第三方Pod的更新,来给我提示,让我重新check这些Pod是否真的需要更新或者是不稳定的版本。之所以说是模糊的,可能确实由于我们确实也不太知道我们需要怎么做,只是有痛点。

不过既然有了痛点,就要去解决。先做出来一般之后在修改。

尝试方案

我收到这个需求的时候,也确实有点懵逼,因为可能最初只是一点抱怨,说的也不明确,我刚开始也没什么思路。不过仔细想了想之后发现,可以把需求整理为2个核心目标。

  1. 检测更新
  2. 通知开发者有变化

检测更新

作为一个iOS开发者我们要熟悉我们使用的工具,我们知道Pod如何来绑定版本的变化,使用的是当前工作目录的Podfile.lock文件,那么我在每次Pod更新前,我用脚本去分析下新旧文件,如果更新了则是有库发送了变化,再去通知开发者。 说起来简单,不过我这种shell 0基础选手怎么办,当然是学了 这里找到了shell30分钟入门教程 说起来30分钟不过我这种笨人学了3个小时才练习完,不过shell脚本确实非常实用,推荐读者去学习下,在平时的开发中确实能帮到自己。

学完shell之后,我写了个脚本要求开发者使用我的脚本进行Pod install 或者 update等。不能再直接终端执行这个命令 。

install 代码逻辑如下


  echo "请输入Pod command 相关参数 "
  echo "1 : install"
  echo "2 : update"
  echo "3 : install --verbose --no-repo-update"
  echo "4 : update --verbose --no-repo-update"
  echo "5 : 自定义参数"
  podcommandParam="install"
  while  read podCommandInputParam
  do
    case ${podCommandInputParam} in
      1)
      podcommandParam="install"
      break
      ;;
      2)
      podcommandParam="update"
      break
      ;;
      3)
      podcommandParam="install --verbose --no-repo-update"
      break
      ;;
      4)
      podcommandParam="update --verbose --no-repo-update"
      break
      ;;
      5)
      echo "请输入自定义参数"
      read podcommandParam
      break
      ;;
      *)
      echo "输入有错请重新输入"
      ;;
    esac

  done


  echo "您选择的是-------${podcommandParam}"

备份逻辑

到这一步我们就可以去做备份功能了。 Q: 为什么备份? A: 每次执行Pod命令 CocoaPod都会进行原地修改,涉及到三个东西 *.xcworkspace Podfile.lock Pods/ ,回忆一下以往执行命令的时候,你执行pod命令的时候可能还报过错,但是发现整个的几千个文件瞬间都发送变化了,真是非常恶心。有了备份之后我们还可以在pod执行错误的时候恢复这三个东西的原来面目,不用我们每次再用sourcetree去重置文件。

Pod 命令执行完有两种情况

  1. 执行成功 ----> 检测更新 --> 删除备份
  2. 执行失败------> 恢复文件,删除备份--->并报错

下面是基本的代码逻辑

function beforePod() {

  #先复制一份原始的lock文件 和 Pods文件夹
  echo "正在备份资源"
  cp  ${oriPodLockName} ${backPodLockName}  >> ${logFile}
  cp -a ${oriPodsDIR}   ${backPodsDIR}  >> ${logFile}
  cp -a ${currentworkSpace} ${backworkSpaceDIR} >> ${logFile}
}
function afterPod() {
  echo "资源后续清理"
  rm ${backPodLockName}  >> ${logFile}
  rm -rf ${backPodsDIR} >> ${logFile}
  rm -rf ${backworkSpaceDIR} >> ${logFile}
}
function recoverPod() {
  echo "正在恢复原始文件"
  mv -f ${backPodLockName} ${oriPodLockName}

  rm -rf ${oriPodsDIR} >> ${logFile}
  cp -a ${backPodsDIR}   ${oriPodsDIR}  >> ${logFile}
  rm -rf ${backPodsDIR} >> ${logFile}
  # mv -f ${backPodsDIR} ${oriPodsDIR}
  rm -rf ${currentworkSpace} >> ${logFile}
  cp -a ${backworkSpaceDIR}   ${currentworkSpace}  >> ${logFile}
  rm -rf ${backworkSpaceDIR} >> ${logFile}

}

检测更新

  echo "-------------------------------当前发生变更的pod库---------------------------------" >> ${diffchangeFile}
  echo "--------------------------------------------------------------------------------" >> ${diffchangeFile}
  for (( i = 0; i < 3; i++ )); do
    echo ""
  done
  ### something
  diff ${oriPodLockName} ${backPodLockName}  -H >> ${diffchangeFile}
  echo "-----------------------------当前发生变更的第三方文件统计----------------------------" >> ${diffchangeFile}
  echo "--------------------------------------------------------------------------------" >> ${diffchangeFile}
  for (( i = 0; i < 3; i++ )); do
    echo ""
  done
  diff ${backPodsDIR} ${oriPodsDIR} -r -B -a | diffstat >> ${diffchangeFile}


  echo "-------------------------当前发生变更的第三方文件变化详细统计-------------------------" >> ${diffchangeFile}
  echo "--------------------------------------------------------------------------------" >> ${diffchangeFile}
  for (( i = 0; i < 3; i++ )); do
    echo ""
  done
  diff ${backPodsDIR} ${oriPodsDIR} -r -B -b >> ${diffchangeFile}

通知开发者

目前我做的是直接打开文件来给开发者看


  open -a Atom     ${diffchangeFile}

  if [  $? != 0 ]
  then
    open -a Xcode ${diffchangeFile}
  fi

  if [  $? != 0 ]
  then
    open  ${diffchangeFile}
  fi

完整演示

这是一个失败的演示:

checkErr

这是一个成功的演示

checkSuccess

后记

后面拿着给业务方的同学看了,业务方感叹效率,觉得做的很快,不过还有几点不足(其实就是不满意喽),。

  1. 我们这么大的团队(30iOS 左右)靠开发者主动使用脚本这个约束并不是特别好,如果有新人入职不知道怎么办,有时候着急忘记了怎么办。
  2. 开发者万一没有仔细看log'怎么办,我们需要一个留存的证据 ,比如邮件,这样在出bug的时候就嘿嘿嘿的甩锅给他喽。

后面的话和安卓的朋友一起沟通说可以放在server端去做,我们使用的CR平台是gerrit,gerrit能检测到开发者merge代码。可以在这个时候去做,检测 并且可以直接利用邮件系统发给开发组的全组同学,大大降低出现风险的机会。 不过作为一次学习的记录还是总结一下分享给大家。 代码地址

阅读更多