从视觉到 App:网易有钱 iOS 项目切图与适配实践

阅读数:6051 2016 年 5 月 23 日

话题:移动AndroidiOS语言 & 开发架构

引言

在 iOS、Android、PC Web 产品开发中,有个步骤基础且重要——切图和适配。在有钱项目开发中,我们参考了诸多流行的切图和适配方案,总结了一套自己的流程和规范。下面以网易有钱 iOS 的开发过程为例,介绍下我们的方法。

切图工具

切图工具五花八门,比较有代表的是 Photoshop+ PxCook +Cutterman 等 Photoshop 配合 Photoshop 配合标记工具、导出工具的方式;另外一种就是 Mac 上的 Sketch+plugins 即 Sketch 配合标记工具、导出工具。下面以 Sketch+Sketch Measure+Sketch Export Generator 为例。

切图需求

传统的切图工作是由视觉设计师来完成的,提供 assets(切图文件夹)的同时还提供了 measurement(视觉标注 )供开发使用。这种方式在我们以前也有使用,但这种方案是有弊端的:

  1. 项目成员里通常是 1 个视觉搭配 1 个 iOS 开发 +1 个 Android 开发 +1 个 PC Web\H5 开发,如果是视觉来做切图(多份适配图)+ 标记,不仅视觉的工作量太大,而且分工饱和度不匹配。如果加上后期需求变更,视觉的工作会暴增,视觉同学不堪重负。

  2. 开发逻辑和视觉逻辑不一致。开发往往会因为切的图和自己实现的思路不一致,而需要进行二次加工或者使用 workround 的方案,非常别扭。如果开发不会切图,可能需要视觉回炉重新切。在我们过往开发过程中明显地感觉到:如果是从 JS 转为专职做页面架构的同学,他们提供的 html 的文件结构层级、切图等让前端开发用起来很舒服——html 结构的层级是匹配实现逻辑的;相反如果是个本行就写页面架构的同学提供给前端开发的稿子,很少会有这样的优势。

在有钱项目开始不久,我们决定对传统的方式做以下调整:

  1. 视觉只需要出规范、Sketch 源文件、关键标记,不需要切图

  2. 开发自行切图,具体元素具体标记

这样视觉同学只需要修改 Sketch 源文件,直接传导给开发同学,开发同学拿到源文件,查看标记 + 切图。以后需求变更,视觉也只需要改 Sketch 源文件和重要标记(大部分是不需要的),就可以直接传导给开发重新切图——之间衔接比较流畅。能够实施这套方案,也得益于 Sketch 的完善和视觉与开发之间的交流深入。

  1. Sketch 比起 Photoshop 更适合移动端(Sketch vs Photoshop 见文末参考链接)。 PhotoShop 在标记方面、导出 1x2x3xPDF 等方面缺乏支持,还有其臃肿的体积、蜗牛般缓慢的打开速度、贪婪的吃内存等问题非常影响使用体验。让开发在切图、看标记的时候,不得不在 PhotoShop、标记工具、插件之间切换,额外消耗精力;相反 Sketch 内置切图、标记功能,开发人员非常容易上手。

  2. 同时视觉同学也从开发这边得到了一些反馈,了解了一些常用的界面的实现原理和逻辑,反馈到视觉设计阶段,作为设计规范和设计原则并遵守。这让视觉和开发对界面布局逻辑有了些许共识,衔接更加顺畅。

下面按照工作流的方式分别说明每一步的规则和规范、同时列一些有钱中的实例,方便读者理解。

第一步——确定视觉规范和基础约定

这部分规范和约定对应传统的 Spec 文件夹的东西。包括但不限于以下:

  1. 约定项目需要的字体字号规范。比如,把普通字符颜色和字号分别定义为 MK_COLOR_TEXT_NORMAL 和 MK_SIZE_FONT_NORMAL,分别对应 0x555555,16f。事实上这只是基础规范。如果考虑到对 iPhone 和 iPad 的版本适配,我们后面有了编号的设计,这个编号代表了字号和颜色。不同的设备下字号是经过适配的,调用者不需要了解具体数值,如 [MKStyleUtil standardLabelStyle:baseInfo withCode:MKFontStyleCode_067];能编号也得益于 Sketch 的 TextStyle 功能,在 Sketch 实现中,叫 SharedTextStyle,精确的表达了其作为规范化的工具的用途。

    图 1. 视觉规范,图示为编号为 67 的 TextStyle

  2. 约定基础原则。这些原则是开发是否能够在后期快速、准确、高效切图,实现视觉到 app 转换的关键。如:

    1. 约定 Sktech 开发以 Rendered Pixels 的尺寸来做,如 750X1334,而不是 375X667。缺点是,Sketch 在导出的时候,尺寸是偏大一倍的。图 2 是自动生成的导出选项,偏大,图 3 是正确的导出选项,需要手动调整。

      图 2. 错误的,大一倍的导出选项

      图 3. 正确的,导出合适图片的选项

    2. 所有元素的尺寸必须为偶数。

      这个约定可以有效避免当元素位置为奇数的时候出现毛边或者锯齿。具体原因是,图片元素在渲染时,元素边界不在物理像素边界上时,该元素会拉伸或者移动到物理像素边界。长宽是奇数 X 偶数的图片,如果旋转转 90 度或 270 度,也容易出现锯齿。

    3. 字符元素不要有 padding,他的高宽应该完全是字符本身的字体和字号来自动撑开的。

      这个规范的需求来自于,当需要确定字符和父元素或者兄弟元素间距的时候,使用 option+click 显示的尺寸数值或者安装 measure 插件之后 ctrl+shift+3 出现的尺寸数值,可以直接在代码中使用。

      图 4. Sketch Measure 插件测量元素尺寸和边距的示意

    4. 良好的分组。

      如果视觉的设计是边框 + 阴影 + 中间的小树是一个整体,那么视觉应该将这些元素作为一个分组。分组不仅让视觉在设计的时候可以作为一个整体复用,也方便开发快速切图——开发只需要选择边框,就能选择所需的元素。这里还有个特殊情况,如果视觉实现的效果是用自己特殊的字体字号设计的,视觉会使用 Sketch 的 Text on Path 技术,将其转化为一个图片。这样开发不需要安装这种字号也可以显示出来,非常棒。良好的分组在使用 measure 插件的时候,control+shift+3 也会生成合适的边距。

    5. 基础约定中的多态变化。 按钮会有 normal,disabled,selected,highlighted 等状态。视觉和开发约定:文字的 disabled 是 normal 的 0.5 透明;一组元素的 disabled 是 normal 的 0.2 透明。这个 0.2 透明通常是一个背景颜色为 #000000 的视图元素覆盖到一组元素的上面,实现 disabled 效果。

      图 5 中,如左图是正常状态,右图是在左图的基础上加了一个 0.2 透明的黑色图层。下图还示例了一个有旋转的图层,切图的时候,有以下原则:如果不涉及到动态旋转,在切图的时候就切出带旋转的图片,目的是避免运行时进行旋转计算,消耗性能。有了 disabled 的约定,很多按钮和图层的样式都不需要视觉出 disabled 的效果(实际中还是有例外)。有了基础的约定,减少视觉和开发的沟通时间,在效果上也保持整个 App 的一致性。切图的第一原则:能不切就不切。

      图 5. 一组元素的 normal 和 disabled 效果对比

    6. 组件之间,一定要有规则的对齐方式。

      左对齐、右对齐、居中、垂直居中、顶部对齐、底部对齐。组件之间的间距可以是任意数值,但组件和屏幕的边距应该是一致的,如 30px。

第二步——视觉和开发的协作

在以前的旧项目开发流程中,为了减少沟通的摩擦,我们把视觉、交互、开发等资料都引入了一个叫一览 query 的工程里,打包为一个 web 服务,在内网使用。这个工程里包括 axure 交互稿的 html 版本、视觉稿 PSD 文件、开发中使用的第三方接口(txt 文件,word 文件),还有项目组的项目列表和对应负责人一览。交互将导出的 html 上传到 query 服务器,生成一个链接,其他人只需要访问这个链接就可以看到最新的交互稿,而且是实时的,不会出现交互和开发看到两个不同版本的问题。

以前是视觉做出一个效果,分别导出 PNG 和 PSD,PNG 作为给产品和 QA 等的参考,PNG 的文件由视觉以 iCloud 里图片共享的方式,供 QA、产品使用,PSD 文件以点对点的(邮件或者 POPO)方式给开发。后期使用过有道云协作共享 PSD。到了 Sketch 时期,我们又使用了百度网盘共享的方式。百度网盘 +Sketch 文件,协作起来容易的多,一是百度网盘可设置为自动同步,没必要每次都手动去 sync,二是 Sketch 文件不大,传送速度也很快。

但是,Sketch 里一个 Page 包含了若干的 artBoard,这些 artBoard 分属不同模块,所以是大家一起共享这个文件。这时候一定要关闭 Sketch 的 autoSave 功能,不然一定会出现一个视觉文件的多个 backup 都在网盘的情况。

所以最佳实践是百度网盘 +sketch without Auto Save + iCloud 的图片共享。

说到视觉和开发的协作衔接问题,不得不称赞 Sketch Measure 插件提供的 Spec export 功能。它的理念:视觉面对 Sketch 源文件,而其它人(iOS,Android,PC Web,产品,QA)直接面对 Spec export 导出的 html 文件。既然是 html 文件,自然可以跨平台,带来的优势是——只有视觉需要购买 Sketch 软件,其它人都只需要浏览器。开发可以在 export 的 html 文件里除了可以查看视觉效果外,还可以看元素的尺寸和间距、元素文字内容、radius、填充色等,还可以导出图片。这意味着什么?一个 iOS 开发完全可以拿 HTML 文件代替 Sketch,因为开发需要的东西都可以在 html 文件里找到。又可以引入到我们的 query 系统,提供在线预览功能,是不是很棒——B/S 模式的精髓!

> 图 6 Sketch Measure 插件的导出效果图

这是理想状态。

现实中,受限于视觉分组的逻辑和开发逻辑的不一致(分组影响导出的 html 里可以导出那些图片),大部分的间距标记其实对开发是无用的。当业务功能较多时,视觉的工作量会变得很重,所以目前 Measure 插件还没用起来。但是如果是简单项目,完全可以达到这种效果——优雅而高效。如果是 PCWeb 项目,用 Sketch 的另外一个插件 marketch 效果更好。Sketch Measure 插件在导出的时候,只能选择当前选中的 artBoard,所以如果需要传导给开发全部的 artBoard,是很麻烦的。我们在 Sketch Measure 的基础上增加了导出选项(当前选中、当页、所有),同时优化了导出页面的左侧导航交互,增加了显示文字的 StyleName 的功能,这就是插件 Sketch Measure Plus。

第三步——开发切图需知和技巧

>>   尺寸的确定,0padding+margin。

宁愿写 magic number 的 margin 值也要保持 0padding。具体体现在文字上,如果是单行文字,设定 padding=0,margin 可采用居中或者和兄弟组件之间的距离为某个具体值 ;而图片呢,则切不包含空白边缘的图标 。

>>   预处理视觉稿

增加容器。视觉稿中有一些相同的东西并排显示, 在视觉设计时,可能不会有容器,但代码实现层面,会用循环来输出这些元素,所以最好的方式是放到一个容器里,这样方便整体定位和更新(如 removeAllSubview) 等。预先判断各个元素的相对定位关系:如按照父容器定位、和兄弟节点定位、居中对齐等;确定定位关系,除了要考虑各元素之间位置有动态变化的情况外,还需要和视觉设计师沟通不同情况下的显示原则,如高度变高后,是所有元素之间的间距平分还是只是撑高某一个间距。

>>   从开发的角度看视觉元素

基本处理元素为图片、文字、容器。开发在拿到视觉稿时,第一时间关注到元素的设计要点,在编写代码的时候注意处理,这样一次 run、build 就能高度还原视觉稿,省去大量重复 run、build 时间。看到图片,关注点:

  1. 是否有圆角,是否需要切图切位圆角(一般切图包含圆角)

  2. 是否有边框,是否要切图切边框(一般切图中包含边框)

  3. 是否半透明

  4. 图片和四周的关系(固定边距或者居中)

而看到文字,关注点:

  1. 字体颜色、字号、字体粗细(font-weight)

  2. 在容器中的对齐方式(居中,居左,居右)

  3. 是否半透明

  4. 图片和四周的关系(固定边距或者居中)

  5. 多行文字还需要注意行距

关于容器,关注点较少:

  1. 是否圆角。

  2. 是否边框

  3. 容器高宽是否自适应。

  4. 背景色

  5. 补白(这个很重要也容易被忽略)

举例说明拿到视觉稿,如何将其分解为一个个小组件,用代码实现。

下图的图分别是理财指南的三种不同情况的视觉稿。当有红包的时候,中间位置被撑开,下半部分向下移;在 iPhone5 以下中显示,警告文字是两行。

图 7 没有红包信息, ‘苹果公司……’的什么没有换行

图 8 有红包信息, ‘苹果公司……’的什么没有换行。

图 9 没有红包信息, ‘苹果公司……’的有换行(实际上是 iPhone4、5 的情况)。

分析一下各个元素的之间的关系。不同的人对各个元素的层次划分,归属关系是不一样的,步骤:

  1. 确定元素个体。分解如下,如果是没有红包则显示编号 3‘的文本元素,有红包则显示编号 3 的文本元素:

    图 10 元素分解图,4 是自行添加的元素,用来管理 3 个产品的容器。

  2. 确定元素的范围。

    注意按照上面的原则,每个元素的尺寸都只包含自己,没有 padding。其中 3 个产品外面的绿色框图(即元素 4)是为了代码实现时方便定位需要的容器,视觉稿里是没有的。所以有了以下的区域划分:

    图 11 各个元素区域划分图

  3. 确定元素的边界之后需要确定元素的定位。

    分析到会有没红包和有红包两种显示效果,差别在于整个 cell 的高度。元素 1、2 保持对容器顶部对齐,元素 8 保持对容器底部对齐;元素 9 是一类元素,和视觉沟通之后,定位原则是:左右和屏幕之间的距离固定,元素之间的间隔相同(之所以没有说固定的值是因为后面有 iPhone5,iPhone6,iPhone6P,iPad 等端适配)。而元素 5、6、7 是 9 的子元素,其中 5 顶部和 9 对齐,6 顶部对齐 5 的底部并偏移,7 顶部对齐 6 的底部并偏移。所以有了以下尺寸标识:

    图 12  元素定位图,边距。

图示中,没有标识横向约束的,表明横向宽度和父容器等宽,居中对齐或者左对齐。如元素 5、6、7 是和父容器等宽;8 和父容器横向有 30px 的 margin 等。

可能其他人会有这样的分组。 将 1、2、3 作为一组,让他们有一个父容器 10,让容器 10、4,8 作为兄弟节点,并且 10、4、8 元素依次向下布局。这样带来的问题是,如果有红包信息,需要撑开高度,那么需要修改容器 10 的高度,同时为了向下移动的部分能够显示完整,还需要修改 Cell 的高度。而目前图示的分组(没有包装 1、2、3 为容器 10,并且 4、8 向下对齐),带来的好处是只需要修改 cell 的高度,就可以实现视觉所示效果。

第四步,适配

有了素材有了尺寸,下一步是代码实现,你必须面对一个问题,适配。

如果你还没有感受到适配是个大问题,那么这个图会让你改变想法。

图 13 iPhone 尺寸大全

从视觉稿到一个可拿在手上触摸的 APP,需要处理(iOS7+iOS8+iOS9)*(iPhone4/5+iPhone6+iPhone6P+iPad)种情况(虽然真正需要处理并没有这么多)。基本适配方案如下:

  1. 文字字号大小,以 iPhone6 为准,iPhone5=iPhone6, iPhone6P = iPhone6*1.5,特殊地方特殊处理图片元素。素材,使用 1x2x3x 作为对应设备使用的图片;尺寸,在不同的设备里有不同的尺寸,图片相应等比缩放。

  2. 对齐和间距。 尽量居中对齐,规范间距(如所有的 tableView 的 cell 高度是一致的,只有两种高度),总结起来说,固定不动项,自适应空白。

  3. 不适配。典型的,所有元素和屏幕的边距固定为 30px,颜色不适配。

说到图片的适配,这里需要说一下 PDF 格式的 assets,据说可以只切一份 PDF 的图片而不需要 3 份。从 xcode6 开始支持以 1x 的尺寸切出 PDF 矢量图,xCode 在 complie 的时候,生成对应的 1x2x3x 的 PNG 图片。 经过我们测试结果发现,毛刺严重,而且这篇文章 Why I don’t use PDFs for iOS assets(https://bjango.com/articles/idontusepdfs/)也印证了目前不适合用 pdf 来代替 1x2x3x 的图片的结论。在查阅了一些资料之后,还发现一些其他限制:

  1. pdf 的图,不能使用 slicing(显而易见)

  2. SVG 或 PDF 矢量格式的资源, 这种方式要额外消耗手机的计算资源,特别是在界面跳转时有转场动效的情况下(未验证)

所以我们还在使用 sketch export 来导出 3 份 PNG 图,如果读者有更好的方案,欢迎和我们分享交流。

代码层面适配是如何实现的呢?

  1. 文字字号在不同的设备下有不同的大小。所以我们为某种类型的字体,设置编号。如图示中的字体被编号为 64,这个编号约定了字体和字号(不包含对齐方式)。

    图 14 Sketch 里的 TextStyle,用于编号

    它代表了这个文字字号的编号是 64,颜色编号是 64。实际上字体编号 64 不是一个固定的值:可见字体适配,而颜色不适配。

    实际编码中,如下所示,就可以完成适配。

  2. 容器的尺寸和边距。下图分别是 iPhone5、iPhone6、iPhone6P 视觉提供的适配图, 适用大部分适配的原则,标记只列出了几个关键的地方。注意:每个答案文字元素始终是水平、垂直居中对齐的。

    图 15 iPhone4、5 的适配效果。图中标出了边距、一个问题行的高度,跳过按钮的尺寸。

    图 16 iPhone6 的适配效果

    图 17 iPhone6P 的适配,可见问答卡片到屏幕的边距已经不是 45px 了,而是特殊的 102px

看到这么多尺寸,是不是有种要哭的冲动?不过有了 masonry 和 StyleUtil,写起来容易得多。以整个问答卡片的定位为例,关键代码如下:

适配是个复杂的工程,即使有了所谓的规范,也不能覆盖所有的场景。有些很重要的场景还是需要一个像素一个像素的调整,终究是个体力活。

说完了我们最核心的方法之后,为了保持文章的完整性,列出切图时命名规则:模块名 - 页面名 - 图片种类 - 图片名字状态(如果有)破折号 - 表示分组,下划线表示状态, 最长破折号不要超过 5 个,如果还有很多子功能,可以使用驼峰模式。

譬如,投资模块 - 股票列表页面 - 头部的背景。命名

invest-stock-bg-tableheader

当总体股票是赚钱的时候,是红色的背景,如果亏损则是绿色,命名:

invest-stock-bg-tableheader_green
invest-stock-bg-tableheader_red

买入按钮的图片,正常和 disabled 状态的命名:

invest-stock-ico-buy
invest-stock-ico-buy_disabled

公共使用的图片,如查看更多,命名:

ico-viewMore

最后一步,走查

如果严格的做好上面的工作,做出 100% 和视觉 matching 的 APP 已经不是梦。然而,然而,人都是有惰性的。可能你记得量了一个块级元素的上下间距适配,却忘记了左右边距适配。

所以为了克服人性的弱点,我们在项目开发的瀑布图中加了一环——视觉交互走查。大概的形式是视觉和开发一对一坐在一起,视觉一个界面一个界面地看,那里不对那里要改,这时候不仅改开发不符合视觉的地方,也改视觉当时没有考虑好的地方。这个阶段有时候很耗时。如果是传统的 native 开发,大部分是按照 iPhone5 走查,改动完毕之后再依次安装到 iPhone6、iPhone6P、iPad,看效果。但是后期部分功能采用了 react-native 的模式,可以做到修改之后,各个设备分别摇一摇就能看效果——做到了多端并行走查,提高了效率。

做完了这些步骤,通过测试之后,一个香喷喷的 APP 就可以 deliver 到用户手上了~

结尾

在追求如何优雅高效地切图和适配的探索上,我们才刚刚起步,有些地方还不尽完善。切图时需要手动改导出选项的问题,还在让我们头疼(Sketch export to xcode 的 change base dentist 不能设置 0.5);Sketch 的 measure 插件在测量边距时,还是不能正确地寻找到它的兄弟元素(手动 option+mouse over 是可以的);在开发 native 代码时,多种设备适配走查,效率还是很低(react-native 没有全部替换 native);对于 iPad 端适配,视觉同学苦不堪言——还不如专门为 iPad 设计一套——怎么适配都很丑(在示例代码中,你可以看到大部分 iPad 是按照 iPhone5 来适配的)。

我们希望看到有更好的方案来做适配这种体力活,解放我们的眼睛,解放我们的双手。对于以上内容,如果存在谬误或者有更好的方法,欢迎读者朋友和我们交流。

参考

  1. https://www.designernews.co/stories/46380-sketch-vs-photoshop-for-ui--ux-design 对比才有看头!SKETCH 秒杀 PS CC 2015 新功能的 7 个地方
  2. https://www.zhihu.com/question/27495264 Sketch 有哪些插件值得推荐?
  3. http://antinomy.me/2014_07_16_186/ 移动 APP 图标设计建议和技巧
  4. http://www.shejipai.cn/app-cut-plan.html  APP 切图的那点事——安卓与 iOS 平台切图小结
  5. http://www.shejidaren.com/image-slice-for-ios-apps.html  iOS APP UI 设计之从效果图到 UI 切图
  6. http://www.ui.cn/detail/48165.html 進擊腾讯の浩男(二)
  7. https://www.zhihu.com/question/25370620 Xcode 6 可以使用 PDF 格式的矢量图作为图片资源,自动适配?
  8. http://youxiputao.tw/articles/3145 iPhone 6/6 Plus 出現後,如何實現一份設計稿支持多個尺寸?

感谢徐川对本文的审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ@丁晓昀),微信(微信号:InfoQChina)关注我们。