iOS

iOS提交后申请加急审核

原文转载自 showwhilllee

之前只是听过加急审核这回事,一直也没有用到过。昨天用了下果然很给力。昨天晚上将近8点(北京时间)提交应用,今天早上上班看了下已经通过了审核,而且可以在AppStore里下载到。

话说加急的原因比较悲催,年前一直在做老版本的更新(一直在改别人代码的bug),临过年一周多的时候说不改了,做新版本的。心里终于舒服些,开始自己写新版本。2月7号上班到2月18号一直在做新版本。就在经理天天催,老板经常问进度的时候,实施那边说xxx那边20号要验收,验收不过要打官司了……

阅读全文»

关于Swift,开发者最需要了解的7个方面



1.在同一个应用中使用Objective-C和Swift进行混编

你可以在现有的项目中添加组件,并使用Swift进行编写;整个项目不需要坚持使用一种语言。同样,我们可以在任何新的Swift应用中快速使用现有的OC编写的库。
2.在开发环境中进行实时动态评估
在开发应用的过程中看看你的代码修改是如何影响app的。你可以通过Xcode的Playground完全控制应用的执行,包括快进、暂停、回放以及重放等。
3.泛型可让你类型化集合
Generics可以让你创建更灵活的代码来处理多个数据类型,而无需代码复用。而且,要处理的对象类型都是已知的,以确保运行时的安全,这样可以尽量减少代码错误。
4.高级运算符
  • 运算符过载(重载):和在C++中一样,你现在可以实现自己的自定义运算符。它已经针对集合和字符串内置。
  • 范围评估:通过值域设定((1……5))和列表评估([‘apple’, ‘banana’, ‘cherry’])简化loop结构。
  • 通过“&”字符进行错误保护:运算符前添加“&”符号可以避免出错。例如,使用“&/”运算符代替“/”避免除以0的错误。
  • Overflow/ underflow检测:检测你的运算符是否会溢出或者下溢,可以利用“&”运算符处理。
5.类型推断
Swift是一种类型化的语言,但是大部分类型声明的责任都由类型推断承担。我们还有一个类型化的安全环境,但是缺少boilerplate代码。
6.类和结构的区别
Swift结构是“value-types”的,这也就意味着当它们在你的代码中传递时,它们总会被复制,但是不参与ARC。这就允许将旧有的C类型结构简单封装为Swift,并且很容易把这些结构传回传统C函数中。
7.默认参数值 
允许函数提供默认参数值以减少过载和代码重复需求。

减小iOS应用程序的大小

本文译自:Reducing the size of my App,由破船翻译,转自:http://beyondvincent.com/

Q: 怎样才能让我的程序安装包小一点,让程序的下载和安装更快速?

A: 本文收集了一些减小程序安装包大小的相关技巧(当第一次下载和安装程序时)。如果是针对升级程序的话,可以看这篇文章(减小iOS应用程序升级时所需下载的大小)(这与第一次安装使用的工作原理有所不同)。

检查应用程序

首先是检查.app bundle,看一下程序包里面哪些文件占的空间最大。

在做任何相关优化之前,我们需要做一些权衡。通过权衡,可以知道把优化的重点集中在什么地方。本文提到的一些技术缺点我们也需要做出考虑,我们必须考虑相关影响,以确保做出正确的决定。如果不做权衡的话,我们无法知道需要对程序做出什么样的改变。

当第一安装iOS程序时,需要下载完整的一个.ipa文件。(注意这不同于升级)。实际上.ipa文件就是一个.zip结构。

我们可以通过这样的方法来找出程序的.ipa文件:从App Store下载应用,然后利用iTunes对iOS设备做同步处理,接着查看目录:~/Music/iTunes/iTunes Music/Mobile Applications,就能找到.ipa文件了。

当让我们也可以通过使用: Xcode的Archive命令来构造出.ipa文件——该文件与提交到App Store上的格式基本一致

检查.ipa文件

简单的将后缀为.ipa文件修改为.zip,然后利用Finder将其解压出来。右键单击解压出来的.app bundle,选择显示包内容,以查看里面的资源文件。通过该方法我们可以看到哪些文件占的空间最大。记住:.app bundle是经过压缩的,并且有些文件的压缩效果要比别的文件好,所以压缩后的效果才是才是最重要的。不过一般情况下在压缩前最大的文件,在压缩后依旧是最大的文件。我们可以将某个文件删除,然后在Finder中右键单击,选择压缩,这样可以更加精确的测量文件压缩效果。

iOS App Store相关因素

作为提交到App Store中app里的可执行文件是被加过密的。加密的副作用是可执行文件的压缩效果没有之前的好了,因为加密会隐藏一些细节问题。因此,从App Store下载下来的.ipa文件大小要比从本地build出来的.ipa文件大。

注意:将长文本内容表数据等从代码中移除,并添加到外部文件中,这样可以减小最终安装包下载的大小——因为这些文件的压缩效果更好。

如果你选择Organizer window中的某个archived,然后点击Estimate Size,Xcode可以对最终分发的程序尺寸做出一个评估。这里并不考虑Mac App Store上面的和企业级部署的iOS程序。

Build Settings

编译选项

将build setting中的Optimization Level设置为Fastest, Smallest [-Os]; 将build setting 中的Strip Debug Symbols During Copy设置为YES(COPY_PHASE_STRIP = YES),这样可以减小编译出二进制文件的尺寸。这里提到的这些设置在Xcode工程中对于Release的配置是默认的。

警告:这些设置会让你的程序很难debug。在一般开发环境build中不建议这样设置,

Target针对较少的CPUs

默认情况下,Xcode工程都配置为:对程序指定的特定CPU类型做优化处理,以生成相对于的可执行文件。不同的硬件,将运行不同的可执行代码。虽然这样优化后的程序,只能针对某些设备运行,但是这大大减小可执行程序的大小。

要想只设定特定类型的CPUs,可以修改build setting中的Architectures,将其从Standard $(ARCHS_STANDARD)修改为你希望支持的列表中对应的特定类型CPU。有效的CPU名称列在Valid Architectures (VALID_ARCHS) build setting中。请不要修改Valid Architectures设置项,最好由Xcode管理。

Assets

对应用程序做一个完整性检查

利用Inspecting Your App中介绍的流程,对.app bundle做一个全面的检查,以了解那些是真正需要用到的。在程序中,经常会包含一些额外的文件,例如readme之类的,这些从来都不会被用到。

将数据从代码中剥离出来

将所有的资源(例如很长的字符串)从代码中剥离出来,并存入外部文件,这样会减小最终文件下载的大小,因为这些文件的压缩效果更好。(参考iOS App Store Specific Considerations中的完整介绍。)

Image Assets

尽量使用8-bit图片

使用8-bit的PNG图片,比32-bit的图片能减少4倍的压缩率。由于8-bit的图片支持最多256种不同的颜色,所以8-bit的图片一般只应该用于一小部分的颜色图片。例如灰度图片最好使用8-bit。

针对32-bit的图片尽量使用高压缩的比率

利用Adobe Photoshop的Save For Web可以减小JPEG和PNG的图片大小。在Xcode中,默认情况下,会自动的使用pngcrush来压缩.png图片。

Audio Assets

音频的压缩

参考WWDC中的Audio Development for Games,里面介绍了如何有效的处理音频。常规来说,我们要使用AAC或MP3来压缩音频,并且可以尝试降低一下音频的比特率。有时候44.1khz的采样是没有必要的,稍微低一点的比特率也不会降低音频的质量。

终极指南 - 怎样为iOS8应用制作预览视频

本文是关于iOS 8应用预览视频的话题,从设计、技术规范,到录屏、编辑工具,介绍的都比较详尽;建议收藏,在接下来用的到的时候作以参考。下面进入译文。

最近这段时间,苹果的世界里出现了很多新东西,比如屏幕更大的iPhone 6,可穿戴设备Apple Watch,iOS8,以及旨在帮助用户更好的发现应用的App Store改版等等。

说到App Store的改版,最值得设计师、开发者和市场人员关注的大概就是视频预览功能了。官方将其称为“应用预览(App Previews)”,如今已经正式出现在iOS8的App Store当中。自然,已经有一大波设计师和开发者为他们的产品制作了预览视频并通过iTunes Connect上传。坦率的说,如果你也有自己的产品,那么也该开始考虑做这件事了。

来自官方的设计制作规范

与截屏图片一样,苹果针对应用预览视频提供了一些官方的设计制作规范。视频在上传之后同样需要接受苹果的审核,这就意味着如果你在预览视频方面破坏了规则,一样会被拒掉,所以建议你首先仔细阅读一下官方的规范。

技术规范

当前,我们可以为每个应用上传两段预览视频,其中一段用于iPhone,一段用于iPad。

从技术角度讲,你需要遵守以下这些规范:

  • 长度在15至30秒之间
  • 通过H.264 MPEG或ProRess 422(HQ)压缩
  • 每秒30帧(30p)
  • 最终文件不超过100MB
  • 扩展名可以是.mov、.mp4或.m4v

分辨率规格如下(单位为像素):

01-ios-8-app-store-preview-video-design-development-guideline.png

此外,上传视频需要基于以下平台及软件:

  • 安装了OS X Yosemite的iMac或MacBook
  • Safari浏览器

设计规范

规范当中的一些内容不大容易解释,因为它们更像是“建议”,而非“规则”。不过要记住,苹果官方会将这些“建议”作为评估的基础;遵守这些规范将能帮你避免遇到那些不必要的麻烦。

设计规范内容较多,我们不妨先来看下概要:

  • 聚焦在最重要的三个卖点上
  • 通过录屏演示产品体验
  • 演示操作时不要让手挡住界面
  • 不要通过编辑视频来造假
  • 内容至少要适合4岁以上的人群
  • 不要使用你不拥有版权的素材
  • 不要依赖于文字介绍
  • 瞄准目标用户群体及其语种
  • 视频中不要涉及价格及运营商信息
  • 不要使用有可能“过期”的内容或素材
  • 从应用中录制实际音效
  • 使用高品质的背景音乐及画外音
  • 选择一张合适的封面

聚焦在最重要的三个卖点上

当然,你不必严格按照字面上的意思强行挑出三个卖点进行演示;重点在于不要试图在30秒的视频中面面俱到,因为用户根本没法理解和记住那么多东西。

另一方面,虽然苹果允许你直接使用30秒的实际操作视频,但也不要将这30秒全部用来演示某一个功能流程,因为这会很无聊,而且可能使用户错过其他一些重要的东西。

用户通常会因为对产品当中一到三个卖点的关注而下载你的应用。卖点在设计上?或是某种新技术?还是对传统界面模式的优化改良?挑出那些最吸引人的亮点,在一段简单的蒙太奇当中展示出来;要做好这件事,你真的不需要拥有多么专业的视频制作能力。

此外,你并不一定需要去展示产品logo,应用图标,或是“Download on the App Store”标识。如果真的有必要,也尽量试着做出一些风格,例如通过某种形式的动画来展示logo和图标,给用户留下更积极的印象,强化品牌识别效应。甚至有公司是专门提供这方面服务的。

通过录屏演示产品体验

苹果真心不希望演示视频看上去像赤裸裸的广告。他们希望让你通过应用自身来更加“诚实”的进行展示。使用实际应用录屏,而不是展示人们拿着手机操作的样子,或是你从手机主屏一路点击进入应用的过程。

对于游戏来说,有取巧的可能,例如只放一些过场动画。苹果同样知道这一点,他们建议你还是尽量放些游戏进行过程中的录屏。

演示操作时不要让手挡住界面

听上去很容易?但对于开发者来说,和苹果打交道就没有什么容易的事。演示视频中的应用界面不要被操作者的手指挡住,尽量通过前面说的录屏来演示;如果确实要展示交互方式,可以在录屏中加入视觉提示元素,例如通过逐渐淡出的圆环代表触摸点。

不要通过编辑视频来造假

这是很严肃的事。不要通过欺骗的手段让人们觉得你的应用比实际的好。除了道德因素以外,这种手段更会为你的产品带来相当负面的评价,一星就是用户用来发泄愤慨的手段。

还要记得按照应用的实际分辨率来显示内容,不要放大。此外,不要把转场效果剪切的让用户误以为实际应用当中有这样的效果。

内容至少要适合4岁以上的人群

任何人都可以访问App Store。我曾经见过3岁的小孩在iPad上通过各种复杂的手势操作应用,熟练的仿佛他在他妈的肚子里就已经开始使用这设备了一样。一旦了解有这种事情存在,你便知道为什么苹果会担心内容的适用人群了。

引用官方的话讲,你应该避免“令人反感的内容,暴力或成人主题,以及脏话。”

不要使用你不拥有版权的素材

我猜这很容易理解。无论是在App Store还是其他地方,尊重版权和商标都是必须的。不要在视频中使用你不拥有版权的音乐、视频、商标、形象、人物形象等等。因为这方面的问题被App Store拒掉可是很蠢的。

苹果官方举了个例子:“如果你的应用可以访问iTunes Library,那么你只能在预览视频中使用自己创作的或是拥有特定授权的音乐。”

不要依赖于文字介绍

至少目前,应用预览视频还不支持本地化,同一个视频会被全球的用户访问到。如果必须阅读视频中的文字才能了解产品功能,那么你就把全世界非英语用户挡在了门外。

如果一定要使用文字介绍,也要注意几点。世界上说英语的人不少。除非你的产品锁定在某个特定的国家或语种上,否则还是用英语的好。保持介绍文字的简短、易理解;因为文字是出现视频当中的,所以还要注意可读性,并确保其出现的时间足够长。

瞄准目标用户群体及其语种

也许你的产品是一款游戏,在全球都有玩家,但最大的玩家群体在中国,而且其中多数是女性。如果这是你要瞄准的目标用户群,那么要对他们所习惯的广告形式及喜爱的功能进行研究,然后制作一段最能为中国女性玩家所接受的预览视频。

视频中不要涉及价格及运营商信息

苹果希望你通过功能,而不是价格,来吸引用户下载应用。坦诚的说,在如今的App Store当中,单纯的免费或低价策略未必能带来多少关注,因为90%的下载都是集中在免费应用上的。况且,用户在你应用图标的下方就能直接看到价格信息,没必要在视频中累述。

你唯一有可能需要在视频中展示价格信息的地方就是关于应用内购买的描述;可以试着在视频中的免责声明部分或是结尾展示这些信息。此外,苹果建议对于包含订阅或账户登录功能的情况也要进行类似的处理。

如果不明确的展示这类信息又会怎样?无非是产品有可能被苹果拒掉。即使通过了审核,也很可能导致用户的差评。这些结果显然都不是你希望看到的。

不过,如果你家应用里有圣诞大促的话又当如何展示呢?其实多数用户不会因为你家宝贝有五折优惠就下载应用。说到这一点...

不要使用有可能“过期”的内容或素材

制作优秀的预览视频是要花不少时间(和钱)的。那么,为什么要给它加上有效期呢?每个人都喜爱圣诞节,但仅限于12月份。如果某些用户在转年2月发现了你的应用,看到了一段圣诞主题的视频,那会有点荒唐吧?不仅如此,这会使用户认为你已经好几个月没有更新产品了。

除非你确定自己会在有特定意义的日期之后很快更新应用及视频,否则不要使用任何有可能过期的内容或素材。当然,如果你的应用本身就是关于圣诞或其他节日主题的,那例外。

从应用中录制实际音效

正如前面所说,要避免在视频中通过手指操作来演示产品的交互特性,而要使用录屏。不过仅凭视觉上的呈现,也许会略显单薄。因此,操作过程中的实际音效对于演示交互反馈特性就变得更加重要了。

使用高品质的背景音乐及画外音

虽说不是必需,但要找到一个合适的人来帮你做些简短的画外音解说,这也不是很困难的事。不过和前面提到的文字介绍性质相同,不要过分依赖于解说。当然有一点需要承认,如果做的够好,那么画外音可以给用户带来非常不错的第一印象。

你也可以自己做这件事,不过要在能够尽量隔绝噪音的地方使用高品质设备进行录制,而不是那种5美元的小麦克风。除非你在录音方面的确内行,否则还是建议你自己写好脚本然后交由外包。记得不要把画外音的风格搞得太像电视广告。

无论是否使用画外音进行解说,背景音乐总是你需要考虑的。如果你有原创的或是经过授权的歌曲,不妨一用。使用音乐配合视频,可以为产品奠定一种基调,让用户在下载前就对大致的体验风格有所感知。对于解说和音乐,要进行充分的效果测试,确保音质如水晶般清澈。

如果你不拥有原创音乐,那么在选择授权音乐的时候一定要考虑到风格是否适宜自己的产品。毕竟,哪怕拥有版权,System of a Down乐队的“B.Y.O.B.”也无法适用于小清新风格的购物应用。

选择一张合适的封面

前文都是聚焦在视频本身上面,我们还忘记了一个小细节:视频不会自动播放,用户需要点击封面才可以观看。所以我们要确保他们有愿望去点击封面才是。

我们可以把这张图片称作封面,也可以叫做海报。它应当来自于视频当中,看上去就像带有一个播放按钮的截屏图片。

上传视频时,iTunes Connect会从视频里自动挑选一帧作为默认的封面,但你也可以自主选择。别忘了做这件事,它对于转化率的提升将起到重要的作用。如果你真的忘记了,而应用已经通过了审核,那么要做好准备重新上传一个新包,哪怕只是为了更换一张封面。

制作预览视频

看过上面所有这些技术与设计规范,是时候动手制作自己的预览视频了。

关于制作方式,苹果官方有做推荐,同时你也有很多其他选择。接下来,我会向各位展示我所探索到的所有的可行方法。无论是个人开发者,还是公司团体,都该试着为自己的产品制作预览视频。继续往下读吧,看看哪种制作方式最符合你们在时间及预算方面的具体情况。

预算较低

独立开发者和小工作室不该错过这场派对。你可能需要花些时间来学习相关的制作软件,但最终仍然可以凭借自己的力量制作出优秀的应用预览视频。

使用苹果自家的软件录制屏幕

这大概是制作录屏的最简单的方法了。OS X Yosemite预置了一项全新的设备录制功能,目的就是帮助广大设计师和开发者制作应用预览视频。此外,你需要带有Lighting接口和Retina屏的iOS设备,系统是iOS 8;iPhone 5及之后的iPhone都可以满足条件。

下面是来自苹果官方的操作指南:

  1. 使用Lighting连接线将你的iOS设备与Mac连接起来。
  2. 在Mac上打开QuickTime播放器。
  3. 选择“文件>新建屏幕录制“。
  4. 在接下来出现的窗口中,选择你的iOS设备作为摄像头及麦克风输入源。

然后就可以开始录制了。

使用第三方软件录制屏幕

另外一个比较简单易行的选择就是TechSmith AppShow。这款新软件也是专门针对应用预览视频的制作需求的,同样要求iOS 8硬件设备以及运行着Yosemite的Mac,当然,还有Lighting连接线。此外,这款软件自带一个简单的视频编辑工具,所以除了像使用Quicktime一样录制屏幕以外,你不需要再使用其它软件来编辑视频了。目前TechSmith AppShow还处于Beta测试阶段,你可以免费参加测试。

当然,你也可以使用市面上其它一些比较成熟的录屏软件。从前,它们的主要用途是从其它设备上录制视频,并在电视或网页上进行播放。

为什么要用这类工具?Reflector的团队告诉我们,通过AirPlay投射的屏幕视频质量更高,因为“通过USB连接的方式录制的视频帧数较低”。TechSmish和Apptamin也对此进行了验证,通过AirPlay投到Yosemite上的视频可以录制出更好的效果,包括iPhone 6和Plus都是这样。

那么这些第三方软件的工作原理是什么呢?他们本身相当于AirPlay接收器,可以将iOS设备的屏幕直接投到PC、Mac或Android上进行录制。确保这些设备在同一网络中,然后在你的iPhone或iPad中打开控制面板,激活AirPlay即可。

下面是一些比较有代表性的第三方软件:

在Mac上直接录制iOS模拟器的屏幕

如果以上方法对你都不适用,还有一招:直接录制Mac的屏幕。不过同故宫这种方式录制的视频质量不是最佳的,帧数不够稳定,你可能需要通过后期编辑来改善。

具体方法就是在Xcode上加载并运行你的应用,然后通过下列软件录制iOS模拟器中的视频:

03-ios-8-app-store-preview-video-design-development-guideline.png

编辑录屏视频

完成屏幕录制之后,你还需要进行编辑,剪掉没用的东西,把精华浓缩到30秒当中。在导出方面,要记得参考前文给到的技术规范。

你需要一些像样的工具来进行编辑工作,下面这些可供参考:

04-ios-8-app-store-preview-video-design-development-guideline.png

如果你使用Final Cut Pro,可以看看苹果官方的教学视频。更多视频编辑工具可以参见Mashable给出的列表

其中一些软件,例如Camtasia,会内置一些编辑工具套装。如果使用这些软件,你需要留意一下这些工具在导出方面是否符合前文给到的技术规范当中的要求。

预算较高

如果钱不是问题,而时间和质量是你们最关注的,那么最好的方式就是外包。下面这些公司可以提供一站式服务,包括屏幕录制、视频编辑、解说等等,让你高枕无忧:

05-ios-8-app-store-preview-video-design-development-guideline.png

小结

预览视频显然比截屏要难制作的多,但成本越高,所能带来的机遇也就越多。制作精良的预览视频可以成为一款优秀应用的又一个标准之一,用户也会越来越多的留意那些提供了预览视频的产品。

对于那些纠结于转化率的提升,或是渴望得到更多机会展示功能的产品来说,预览视频将成为好帮手。游戏类的产品很容易通过截屏来吸引用户的目光,因为它们本身就很炫酷;而对于生产力和效率类的应用来说,要做到吸引人就不那么容易了。花些心思打造一段预览视频将能帮你更好的展示产品。

接下来会有越来越多的设计师和开发者投入到预览视频的制作工作当中,千万别落后。当然,其他方面例如应用图标和静态截屏的优化也是要考虑到的,这些都是提升下载量的重要手段

iOS 7 中实现模糊效果





本文译自iOS 7 Blur Effects with GPUImage

iOS 7在视觉方面有许多改变,其中非常吸引人的功能之一就是在整个系统中巧妙的使用了模糊效果。许多第三方应用程序已经采用了这样的设计细节,并以各种奇妙的和具有创造性的方式使用它。

本文将通过几种不同的技术来实现iOS 7中的模糊效果,当然,这一切都利用了一个名为GPUImage的框架。

GPUImage是由Brad Larson创建的,它利用GPU,使在图片和视频上应用不同的效果和滤镜变得非常的容易,同时它还拥有出色的性能,并且它的性能要比苹果内置的相关APIs出色。

注意:本文需要一台物理设备来编译并运行示例程序(在模拟器上无法使用)。同样还需要一个iOS开发者账号。如果你还没有开发者账号的话,可以来[这里](https://developer.apple.com/)注册一个。注册为开发者之后,会有许多福利哟,例如可以使用物理设备来开发程序,提前获得苹果的相关测试版程序,以及大量的开发资源。

iOS中利用GPUImage实现模糊效果

下面我们先来看看本文的目录结构:

  • 开始
  • 为什么要是用模糊效果
    • 深度引导
    • 上下文
    • 关注度
  • 添加静态的模糊效果
    • 创建截图Category
    • 利用断点测试截屏图片
    • 显示截屏图片
    • 设置contentsRect
    • 重置模糊滤镜
    • 对其背景图片
  • 实时模糊
  • 线程中简洁的分支
  • 一些潜在的实时模糊方案
  • 一个折中的方法——对视频实时模糊
    • 利用GPUImage对视频进行模糊处理
  • 何去何从?

开始

首先先来这里下载本文的starter工程,并将其解压出来。

用Xcode打开Video Blurring.xcodeproj,并将工程运行到设备中。此时看到程序的效果如下所示:



点击屏幕左上角的菜单(三条横纹),可以看到界面中出现两个选项:录制视频和播放已有视频。

请注意,现在所有的用户界面都有一个灰色的背景,是不是感觉有点沉闷呢,本文我们就利用iOS 7中的模糊效果来替换掉这些沉闷的灰色背景。

为什么要是用模糊效果

除了外观看起来很棒以外,模糊效果还可以让程序给用户带来3个重要的概念:深度引导、上下文和关注度。

深度引导

在用户界面上,模糊效果可以给用户提供一个深度引导效果,并且有利于用户对程序导航的理解。在之前的iOS版本中的深度引导效果是通过:三维斜面(three-dimensional bevels)和有关泽的按钮(反映出一个模拟的光源),而在iOS 7中是通过模糊和视差(parallax)来实现的。

这里说的视差效果,可以很明显的观察出来:在装有iOS 7的设备中,将设备从一侧倾斜至另一侧,会发现设备中的图标在移动(会独立于背景)。这样可以给用户做出一个提示:界面是由不同的层构成的,并且重要的界面元素是在最前面的——这也涉及到下面将要介绍的一个概念:上下文。

上下文

上下文可以让用户在程序内获得一种轴承的感觉。动画的过度效果就提供了一种非常优秀的上下文,当用户点击一个按钮时,在两个view之间利用动画效果来切换画面(而不是直接显示一个新的view),可以让用户知道新的view是从哪里出现的,并且可以让用户很容易知道如何回到上一个view。

模糊效果可以将上一个view当做背景显示出来,尽管上一个view已经失去焦点了,不过可以给用户提供更多的上下文:刚刚是在哪里。通知中心就是一个非常棒的例子:当拉下通知中心时,我们可以在背景中看到原来的view(即使现在正在处于通知中心界面)。

关注度

让界面更加关注于某些选择项上,而移除不需要的内容,让用户可以更加快捷的进行导航。用户可以本能的忽略那些被模糊的界面元素,而将注意力集中到某些界面元素中。

通过本文,你将学到两种模糊类型的实现方法:静态模糊和动态模糊。静态模糊代表着快照的时间点,它并不能反映被模糊界面元素的变化。大多数情况下,使用静态模糊效果就足够了。相反,动态模糊则是对需要模糊的背景做出实时更新。

相信看到具体的效果才是最好的,下面我们就来看看模糊效果的具体实现吧!

添加静态的模糊效果

创建一个静态模糊效果首先是将当前屏幕中的view转换为一幅图片。获得图片之后,只需要对图片做模糊处理就可以了。将view转换为一幅图片(截屏)苹果已经提供了一些非常棒的APIs了,并且在iOS 7中又有了新的方法可以让截屏更加快速。

这些新的方法属于截屏APIs中的一部分,截屏APIs不仅可以对某个view截屏,还能把整个view层次截屏,如果你希望对某个view截屏,那么可以把view中的按钮、标签、开关等各种view也进行截屏。

此处我们将截屏的逻辑实现到UIView的一个category中。这样一来,我们就可以很方便快捷的将任意的view(以及view中的内容)转换为一个图片——也算是代码的重用吧。

创建截图Category

打开File/New/File...,然后选择iOS/Cocoa Touch/Objective-C category,如下图所示:



将这个category命名为Screenshot,并将它的category选为UIView,如下图所示:



将下面这个方法声明到UIView+Screenshot.h中:

-(UIImage *)convertViewToImage;

接着将如下方法添加到UIView+Screenshot.m中:
-(UIImage *)convertViewToImage
{
    UIGraphicsBeginImageContext(self.bounds.size);
    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}

上面的方法中,首先调用了UIGraphicsBeginImageContext(),最后调用的是UIGraphicsEndImageContext(),这两行代码可以理解为图形上下文的一个事物处理过程。一个上下文可以理解为不同的概念,例如屏幕,或者此处可以理解为一幅图片。这里的两行代码起到一个离屏画布的作用——可以将view绘制上去。

drawViewHierarchyInRect:afterScreenUpdates:方法利用view层次结构并将其绘制到当前的上下文中。

最后,UIGraphicsGetImageFromCurrentImageContext()从图形上下文中获取刚刚生成的UIImage

现在,我们已经完成了category的实现,接着我们只需要在使用到的地方将其import一下即可。

如下代码所示,将代码添加到DropDownMenuController.m顶部:

#import "UIView+Screenshot.h"

同时,将如下方法添加到相同的文件中:

-(void)updateBlur
{
    UIImage *image = [self.view.superview convertViewToImage];
}

上面的代码确保是对superview进行截屏,而不仅仅是当前的view。不这样做的话,截屏获得的图片只是menu本身。

利用断点测试截屏图片

为了测试截屏的效果,我们在convertViewToImage调用的下面一行添加一个断点。这样当命中断点时,程序会在断点中暂停执行,这样我们就可以看到截屏的图片,以此确保截屏代码的正确性:



在测试之前还有一件事情需要做:调用上面这个方法。

找到show方法,并在addToParentViewController下面直接调用一下updateBlur

-(void)show {
    [self addToParentViewController];

    [self updateBlur]; // Add this line

    CGRect deviceSize = [UIScreen mainScreen].bounds;

    [UIView animateWithDuration:0.25f animations:^(void){
        _blurView.frame = CGRectMake(0, 0, deviceSize.size.height, MENUSIZE);
        _backgroundView.frame = CGRectMake(0, 0, _backgroundView.frame.size.width, MENUSIZE);
    }];
}

编译并运行程序,点击菜单按钮,可以看到Xcode在断点出停止了,如下所示:



在debugger左下角hand pane中选择image,然后单击快速查找图标按钮,就可以预览刚刚的截屏啦:



如上图所示,正是我们所预期的。

显示截屏图片

将截取到的图片显示到菜单的背景中就是小菜一碟啦。

一般来说我们都会利用UIImageView来显示一幅图片,而由于我们要利用GPUImage来模糊图片,所以需要使用GPUImageView

在这里的工程中,已经添加好了GPUImage框架,我们只需要将头文件import一下即可。

将下面的代码添加到DropDownMenuController.m顶部:

#import <GPUImage/GPUImage.h>

注意:GPUImage被包含在一个框架中,所以在import语句中,需要利用尖括弧,而不是双引号

此时,有一个_blurView,类型为UIView——是菜单的灰色背景。将UIView修改为GPUImageView,如下所示:

@implementation DropDownMenuController {
    GPUImageView *_blurView;
    UIView *_backgroundView;
}

修改之后,Xcode会报一个warning:大意是你利用UIView进行实例化,而不是预期的GPUImageView

可以通过下面的方法消除这个警告,在viewDidLad中修改做如下修改:

_blurView = [[GPUImageView alloc] initWithFrame:CGRectMake(0, 0, deviceSize.size.height, 0)];

紧随其后,将如下两行代码添加进去,并移除设置背景色的代码:

_blurView.clipsToBounds = YES;
_blurView.layer.contentsGravity = kCAGravityTop;

clipToBounds属性设置为YES,把超出_blurView范围的子view隐藏起来,而contentsGravity确保图片出现在image view的顶部。

由于_blurView已经用于背景了,所以此处不需要额外设置了。

接着,我们还需要声明一个用于模糊效果的过滤器。

将如下代码添加到DropDownMenuController.m:文件的@implementation中:

GPUImageiOSBlurFilter *_blurFilter;

找到之前添加的断点,右键单击,并选中Delete Breakpoint



下面是非常重要的一步了——初始化模糊滤镜。将如下代码添加到DropDownMenuController.m中:

-(void)updateBlur
{
    if(_blurFilter == nil){
        _blurFilter = [[GPUImageiOSBlurFilter alloc] init];
         _blurFilter.blurRadiusInPixels = 1.0f;

    }

    UIImage *image = [self.view.superview convertViewToImage];
}

注意:上面将模糊半径设置为一个像素,这里暂时将这个值设置低一点,这样可以确保图片的正确定位,当一切ok之后,再增加模糊半径即可。

下面是时候将图片显示到GPUImageView中了。不过并不是简单的实例化一个UIImage,并将其添加到GPUImageView中。首先需创建一个GPUImagePicture

将如下代码添加到updateBlur方法的底部:

GPUImagePicture *picture = [[GPUImagePicture alloc] initWithImage:image];

至此,我们获得了一个图片,模糊滤镜和iamge view。

接着再将如下代码添加到updateBlur底部:

[picture addTarget:_blurFilter];
[_blurFilter addTarget:_blurView];

[picture processImage];

上面这几行代码,就像胶水一样,将所有的事情关联起来。将滤镜当做target添加到图片中,然后将image view当做滤镜的target。

上面代码对图片的处理全程发生在GPU上,也就是说当进行模糊计算和显示时,并不会影响到用户界面。当处理结束时,会把图片显示到image view上面。

编译并运行程序,点击菜单按钮,可以看到如下类似画面:



上面的图片看起来是不是有点奇怪?看到的图片被缩放到适配到菜单视图中了。要对此做出修正,我们需要指定图片的哪一部分需要显示在GPUImageView中——也就是处理截屏视图的上半部分。

设置contentsRect

按照如下代码所示修改DropDownMenuController.m文件中的show方法:

-(void)show
{
    [self addToParentViewController];

    [self updateBlur];

    CGRect deviceSize = [UIScreen mainScreen].bounds;

    [UIView animateWithDuration:0.25f animations:^(void){
        _blurView.frame = CGRectMake(0.0f, 0.0f, deviceSize.size.height, MENUSIZE);
        _backgroundView.frame = CGRectMake(0.0f, 0.0f, _backgroundView.frame.size.width, MENUSIZE);
        _blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, MENUSIZE / 320.0f); // Add this line!
    }];
}

通过指定_blurView.layer.contentsRect来定义一个矩形,在单元坐标空间(unit coordinate space)中,表示只使用layer content的一部分。

编译并运行程序,点击菜单按钮,会看到如下图所示效果:



虽然已经使用了图片的一部分,看起来还是不正确,这是因为它的缩放比例还不适合!此处还缺少对正确内容的缩放。

将下面这行代码添加到show方法中动画block的尾部:

_blurView.layer.contentsScale = (MENUSIZE / 320.0f) * 2;

contentsScale属性声明了layer在逻辑坐标空间(以点为单位)和物理坐标空间(以像素为单位)之间的映射关系。更高比例因子表示在渲染layer时,一个点代表着多个像素点。

编译并运行程序,点击菜单按钮,可以看到缩放比例已经正常了:



没错——看起来好多了!现在关闭程序,然后重新打开,ou~~发生了什么?如下图所示:



看起来这还是有点问题。如果在对view进行animation之前将contentScale设置回2.0,会解决half bar的问题。

将如下代码添加到DropDownMenuController.mshow方法里面的animation block上面:

_blurView.layer.contentsScale = 2.0f;

编译并运行程序,然后点击菜单,接着关闭菜单,再打开菜单,此时菜单开起来如下所示:



现在半个尺寸的黑色box已经没有问题了——但是现在是全尺寸的黑色box!

重置模糊滤镜

上面问题产生的原因是由于进行了二次模糊计算。解决的方法是移除模糊滤镜中的所有target。如果不这样做的话,之后对滤镜的调用不会输出任何的内容——进而引起黑色box的问题。

按照如下代码更新一下updateBlur方法:

-(void)updateBlur
{
    if(_blurFilter == nil){
        _blurFilter = [[GPUImageiOSBlurFilter alloc] init];
        _blurFilter.blurRadiusInPixels = 1.0f;
    }

    UIImage *image = [self.view.superview convertViewToImage];

    GPUImagePicture *picture = [[GPUImagePicture alloc] initWithImage:image];
    [picture addTarget:_blurFilter];
    [_blurFilter addTarget:_blurView];

    [picture processImageWithCompletionHandler:^{
        [_blurFilter removeAllTargets];
    }];
}

上面的代码用processImageWithCompletionHandler:替换了processImage方法。这个新的方法有一个completion block,当image 处理结束时,会运行这个block。一旦image处理结束,我们就可以安全的将滤镜中的target全部移除。

编译并运行程序,点击菜单,检查一下黑色box问题是不是已经解决掉了:



多次打开和关闭菜单,确保之前的那个bug已经解决掉啦!

现在仔细观察一下打开菜单的模糊效果——有些东西看起来不正确。为了更加明显的观察到问题,我们减慢动画的时间,让其慢慢的移动。

show方法中,将animation bloc的持续时间修改为10.0f。

编译并运行程序,点击菜单,然后观察一下菜单出场的慢动作:



恩,现在可能你已经发现问题了。被模糊的图片从顶部往下滑动——而我们的本意是希望模糊效果从上往下滑(并不是图片本身)。

对其背景图片

此处我们需要对静态模糊效果使用一些技巧。当出现菜单时,我们需要利用背景来将模糊效果对其。所以在这里我们不是对image view做移动处理,而是需要对image view做扩展处理,从0开始扩展至image view的全尺寸。这样就可以确保菜单打开时,图片依然保留在原位。

show方法中,我们已经将菜单打开至全尺寸了,所以现在只需要将contentRect的高度设置为0即可(当image view首次创建并隐藏的时候)。

将下面的代码添加至DropDownMenuController.m文件的viewDidLoad方法中——在_blurView初始化的下方:

_blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, 0.0f);

同时,在相同的一个文件中,将下面的代码添加到animation block的尾部:

_blurView.layer.contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, 0.0f);

contentRect属性是可以动画方式设置的。因此在动画期间会rect会自动的插补上。

编译并运行程序。可以看到,问题已经解决:



这样看起来自然多了。现在我们已经有一个具有模糊背景的滑动菜单了。

现在是时候把动画所需时间调整一下了(为了更好的效果,其实之前设置的值是为了测试所用):设置为0.25秒,接着在updateBlur方法中将_blurFilter.blurRadiusInPixels设置为4.0f

编译并运行程序,多次打开菜单,看看效果如何:

实时模糊

实时模糊涉及到的技术具有一定的难度,有些难点需要解决才行。为了有效的进行实时模糊,我们需要不停(每秒60帧)的截屏、模糊计算和显示。使用GPUImage每秒中处理60张图片(模糊并显示图片)一点问题都没有。

真正棘手的问题是什么呢?如何实时的截取屏幕图片,信不信由你!

由于截取的屏幕是主用户界面,所有必须使用CPU的主线程来截屏,并将其转换为一幅图片。

提醒:如果事物每秒钟的变化速度在46帧以上,那么人眼就无法识别出来了。这对于开发者来说也是一种解脱——现代处理器在各帧之间可以完成更多的大量工作。

线程中简洁的分支

当运行程序时,会执行大量的指令列表。每一个指令列表都运行在各自的线程中,而我们又可以在多个线程中并发运行各自的指令列表。一个程序在主线程中开始运行,然后会根据需要,创建新的线程,并在后台执行线程。如果之前你并没有管理过多线程,你可能在写程序的时候总是在主线程中执行指令。

主线程主要处理与用户的交互,以及界面的更新。确保主线程的响应时间是非常关键的。如果在主线程上做了太多的任务,你会明显的感觉到主界面响应迟钝。

如果你曾经使用过Twitter货Facebook,并滚动操作过它里面的内容,你可能已经感觉到后台线程在执行操作了——在滚动的过程中,并不是所有的个人图片立即显示出来,滚动过程中,程序会启动后台线程来获取图片,当图片获取成功之后,再显示到屏幕中。

如果不使用后台线程,那么table view的滚动过程中,如果在主线程上去获取个人图片,会感觉到table view被冻结住了。由于图片的获取需要一些时间,所以最好将这样耗时的操作让后台线程来做,这样就能对用户界面做平滑的操作和响应了。

那么对本文的程序有什么影响呢?之间介绍了,UIView的截屏APIs操作必须在主线程中运行。这就意味着每次截屏时,整个用户界面都会被冻结中。

对于静态模糊效果时,由于这个截屏操作很快,你不会感觉到界面的冻结。并且只需要截屏一次。然而在实时模糊效果中需要每秒中截屏60次。如果在主线程中做这样频繁的截屏操作,那么animation和transition会变得非常的迟钝。

更糟糕的时,如果用户界面复杂度增加,那么在截屏过程中就需要消耗更多的时间,那么就会导致整个程序无法使用了!

那么怎么办呢!

一些潜在的实时模糊方案

这里有一个关于实时模糊方案:源代码开源的live blur libraries,它通过降低截屏的速度来实现实时模糊效果,并不是使用每秒截屏60次,可能是20、30或者40次。即使看起来没有多大区别,但是你的眼睛还是能发现一定的迟钝——模糊效果并没有跟程序的其它部分同步起来——这样一来,界面看起会没有模糊效果更加的糟糕。

实际上苹果在它们自己的一些程序中处理实时模糊并不存在类似的问题——但是苹果并没有公开相关的API。在iOS 7中UIView的截屏方法,相比于旧方法,性能有了很大的提升,但还是不能满足实时模糊的需求。

一些开发者利用UIToolbar的模糊效果来做一些不好的操作。没错,这是有效果的,但是强烈建议不要在程序中使用它们。虽然这不是私有API,但是这并不算是一种可行的方法,苹果也可能会reject你的程序。也就是说在,在之后的iOS 7版本中,并不能保证还能正常使用。

苹果可以在任何时候对UIToolBar做出修改,或许你的程序就有问题了。在iOS 7.0.3更新中,苹果的修改已经影响到UIToolbar和UINavigationBar了,有些开发者也因此报告出利用相关模糊效果已经失效了!所以最好不要陷入这样潜在的陷阱里面!

一个折中的方法——对视频实时模糊

OK,此时你可能在想,要想在程序中做到实时模糊是不可能的了。那么还有什么方法可以突破限制,做到实时模糊效果呢?

在许多场景中,静态模糊是可以接受的。上一节中,我们对view做适当的修改,让用户看起来是对背景图做的实际模糊处理。当然,这对于静止不动的背景是合适的,并且还可以在模糊背景上实现一些不错的效果。

我们可以做一些实验,看看能不能找到一些效果来实现之前无法做到的实时模糊效果呢?

有一个方法可以试试:对实时视频做模糊处理,虽然截屏是一个非常大的瓶颈,但是GPUImage非常的强大,它能够对视频进行模糊(无论是来自摄像头的视频或者已经录制好的视频,都没问题)。

利用GPUImage对视频进行模糊处理

利用GPUImage对视频的模糊处理与图片的模糊处理类似。针对图片,我们实例化一个GPUImagePicture,然后将其发送给GPUImageiOSBlurFilter,接着再将其发送给GPUImageView

类似的方法,对于视频,我们使用GPUImageVideoCameraGPUImageMovie,将后将其发送给GPUImageiOSBlurFilter,接着再将其发送给一个GPUImageViewGPUImageVideoCamera用于设备中的实时摄像头,而GPUImageMovie用于已经录制好的视频。

在我们的starter工程中,已经实例化并配置好了GPUImageVideoCamera。现在的任务是将播放和录制按钮的灰色背景替换为视频的实时滤镜效果。

首先是将此处提供的灰色背景实例UIView替换为GPUImageView。完成之后,我们需要调整每个view的contentRect(基于view的frame)。

这听起来对每个view都需要做大量的工作。为了让任务变得简单,我们创建一个GPUImageView的子类,并把自定义的代码放进去,以便重用。

打开File/New/File…,然后选择iOS/Cocoa Touch/Objective-C class,如下所示:



将类命名为BlurView,继承自GPUImageView,如下图所示:



打开ViewController.m文件,将下面的import添加到文件顶部:

#import "BlurView.h"

还是在ViewController.m中,在@implementation中找到_recordView_controlView的声明,将其修改为BlurView类型,如下所示:

BlurView *_recordView; //Update this!
UIButton *_recordButton;
BOOL _recording;

BlurView *_controlView; //Update this too!
UIButton *_controlButton;
BOOL _playing;

然后按照如下代码修改viewDidLoad方法:

_recordView = [[BlurView alloc] initWithFrame:
                CGRectMake(self.view.frame.size.height/2 - 50, 250, 110, 60)]; //Update this!
//_recordView.backgroundColor = [UIColor grayColor]; //Delete this!

_recordButton = [UIButton buttonWithType:UIButtonTypeCustom];
_recordButton.frame = CGRectMake(5, 5, 100, 50);
[_recordButton setTitle:@"Record" forState:UIControlStateNormal];
[_recordButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[_recordButton setImage:[UIImage imageNamed:@"RecordDot.png"] forState:UIControlStateNormal] ;
[_recordButton addTarget:self
                  action:@selector(recordVideo)
        forControlEvents:UIControlEventTouchUpInside];

[_recordView addSubview:_recordButton];
_recording = NO;

_recordView.hidden = YES;
[self.view addSubview:_recordView];


_controlView = [[BlurView alloc] initWithFrame:
                 CGRectMake(self.view.frame.size.height/2 - 40, 230, 80, 80)]; //Update this!
//_controlView.backgroundColor = [UIColor grayColor]; //Delete this!

接着,需要创建模糊图片,将其显示到上面构建的image view中。回到@implementation中,将下面的两个声明添加进去:

GPUImageiOSBlurFilter *_blurFilter;
GPUImageBuffer *_videoBuffer;

现在你已经知道GPUImageiOSBlurFilter的作用了,那么GPUImageBuffer的作用是什么呢?它的任务是获取视频的输出,并获取每一帧,这样我们就可以方便的对其做模糊处理。一个额外的好处就是它可以提升程序的性能!

一般来说,视频输出的内容会通过模糊滤镜处理,然后发送到背景视图中(被显示出来)。不过,在这里使用buffer的话,发送到buffer的视频输出内容,会被分为背景视图和模糊滤镜。这样可以对视频的输出显示做到平滑处理。

将下面的代码添加到viewDidLoad方法的顶部(在super调用的后面):

_blurFilter = [[GPUImageiOSBlurFilter alloc] init];

_videoBuffer = [[GPUImageBuffer alloc] init];
[_videoBuffer setBufferSize:1];

还是在同一个文件中,将如下高亮显示的语句添加到useLiveCamera方法中:

-(void)useLiveCamera
{
    if (![UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No camera detected"
                                                        message:@"The current device has no camera"
                                                       delegate:self
                                              cancelButtonTitle:@"Ok"
                                              otherButtonTitles:nil];
        [alert show];
        return;
    }

    _liveVideo = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset1280x720
                                                     cameraPosition:AVCaptureDevicePositionBack];
    _liveVideo.outputImageOrientation = UIInterfaceOrientationLandscapeLeft;

    [_liveVideo addTarget:_videoBuffer];           //Update this
    [_videoBuffer addTarget:_backgroundImageView]; //Add this
    [_videoBuffer addTarget:_blurFilter];          //And this
    [_blurFilter addTarget:_recordView];           //And finally this

    [_liveVideo startCameraCapture];

    _recordView.hidden = NO;
    _controlView.hidden = YES;
}

上面的模糊背景是用于录制按钮的。对于播放按钮也要做类似的处理。

将下面的代码添加到loadVideoWithURL:方法中(在_recordedVideo.playAtActualSpeed = YES;之后):

[_recordedVideo addTarget:_videoBuffer];
[_videoBuffer addTarget:_backgroundImageView];
[_videoBuffer addTarget:_blurFilter];
[_blurFilter addTarget:_controlView];

编译并运行程序,打开录制操作,看看情况如何:



好消息是看起来基本正常!坏消息是整个屏幕被缩放到录制按钮中去了。这个问题跟之前遇到的类似。我们需要给BlurView这是适当的contentRect

打开BlurView.m,用下面的代码替换掉initWithFrame:方法:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        CGRect deviceSize = [UIScreen mainScreen].bounds;
        self.layer.contentsRect = CGRectMake(frame.origin.x/deviceSize.size.height,
                                             frame.origin.y/deviceSize.size.width,
                                             frame.size.width/deviceSize.size.height,
                                             frame.size.height/deviceSize.size.width);
        self.fillMode = kGPUImageFillModeStretch;
    }
    return self;
}

contentRect的每个参数必须在0.0f1.0f之间。在这里只需要利用view的位置除以屏幕的size,得到的值即可。

编译并运行程序,看看效果如何:



恭喜!至此已经完成了静态模糊和实时视频模糊的实现。现在你已经完全可以在程序中添加iOS 7的模糊效果啦!

何去何从?

可以在这里下载到完整的工程。

本文不仅指导你在程序中使用iOS 7的模糊效果,还介绍了如何使用GPUImage框架,这个框架也是我非常希望你能看到的东西。重要的是,本文指出了为什么要使用模糊,什么时候使用模糊效果是合适的,这在iOS 7的新设计语言中是一个关键的概念。当然也希望在未来的版本中,苹果能够将相关APIs提供给开发者使用,不过在那之前,GPUImage是一个不错的替代品。

iOS 8人机界面指南(二):设计策略

原文转自:腾讯ISUX (http://isux.tencent.com/ios8-human-interface-guidelines-design-strategies.html)

cover1

2.1 设计原则(Design Principles)

2.1.1 美学完整性(Aesthetic Integrity)

美学完整性并不评判应用的视觉设计,或者用来描述应用的风格特征。美学完整性是指一款应用的视觉表现和交互行为在与功能结合后所传达出的整体一致性。

[ISUX转译]iOS 8人机界面指南(二):设计策略

人们关心应用是否提供了应有的功能,但是也会潜移默化甚至是很直接地被应用的视觉表现和交互行为所影响。举个例子,一款协助用户完成任务的应用,可以通过使用精美而又无干扰的装饰性元素、标准的控件和可预期的交互行为来帮助用户聚焦在任务本身上。这样,应用就能传达出清晰一致的信息,使得人们信任它。但是,如果应用使用干扰的、琐碎或随意的UI来呈现任务,那么人们可能会对其可靠性和可信赖度产生怀疑。

另一方面,在沉浸式应用中—例如游戏—用户期待惊艳的视觉表现,为用户带来乐趣和刺激,并鼓励用户进行探索。人们不希望在游戏中完成严肃的或生产力式的任务,他们会期待游戏的视觉表现和交互行为能够整合进任务当中。

2.1.2 一致性(Consistency)

一致性可以让人们在一款应用中的不同部分甚至不同应用间复用他们的知识和技能。一款具备一致性的应用不应盲从地复制其他应用,也不应在风格上一成不变。应用应专注于让人们觉得舒适的标准和范例,并提供应用内部统一的体验。

[ISUX转译]iOS 8人机界面指南(二):设计策略

在决定一款iOS应用是否要遵守一致性原则时,请思考如下问题:

  • 应用是否和iOS标准一致?是否正确地使用了系统提供的控件、视图和图标?是否以用户所预期的方式整合了设备的特性?
  • 应用是否内部统一?文案是否使用了一致的措辞和风格?同样的图标是否表意相同?在不同的位置执行同样的操作时,人们是否能能预期会发生什么?
  • 应用是否和先前的版本保持一致?条款和意义是否保持不变?基本概念和主要功能是否发生了变化?

2.1.3 直接操作(Direct Manipulation)

当人们不再使用一堆控件进行操作,而是直接操作屏幕上的对象时,他们能更集中精力完成任务,也更容易理解这些行为所产生的结果。

[ISUX转译]iOS 8人机界面指南(二):设计策略

使用多点触摸界面,人们可以通过捏合操作来直接放大和缩小图片或文本内容。在游戏中,玩家应该直接移动和作用在屏幕上的对象。例如,游戏中可能会显示密码锁,用户可以通过转动它来打开。

在一款iOS应用中,如下情况中人们应该能够进行直接操作:

  • 旋转或者移动设备来影响屏幕上的对象
  • 使用手势来操作屏幕上的对象
  • 显示即时可视的操作反馈

2.1.4 反馈(Feedback)

反馈可以明示人们的行为,展示操作的结果,并更新于任务进程之中。

[ISUX转译]iOS 8人机界面指南(二):设计策略

iOS内置的应用对用户的每个行为都提供了可感知的反馈。当人们点击列表项和控件时,它们会被临时高亮,并会在操作过程中持续一段时间,以此展示控件被执行的过程。

精细的动画会给人们带来有意义的反馈,帮助阐明行为的结果。例如,列表中新增一项时的动画可以从视觉上帮助人们发现列表的变化。

音效同样可以为人们提供有效的反馈,但不应成为唯一的反馈方式,因为人们不一定能听到。

2.1.5 隐喻(Metaphors)

当应用中的虚拟对象和交互行为与用户已经熟悉的体验相似时—无论这些体验是来源于真实或数字生活—用户就可以快速地掌握如何来使用这个应用。

当应用使用隐喻来传达某种用法或体验时,最好不要让隐喻突破所依赖的对象或交互行为本身的限制。(译者注:此处可理解为对于隐喻的使用应量力而为,不要过于牵强。)

由于人们实际上是和屏幕进行物理上的交互,所以iOS应用有很大的余地来使用隐喻。iOS中的隐喻包括:

  • 移动分层视图来显示被遮挡的内容
  • 拖曳、轻扫和滑动游戏中的对象
  • 点击开关,滑动滑块,转动选择器
  • 轻扫来翻阅书本或杂志

2.1.6 用户控制(User Control)

是人—而不是应用—发起和控制行为。应用可以对用户进行操作建议,对有危害的后果予以警示,但是不应替用户来做决策。好的应用会在给人们帮助他们避免不必要结果的能力时,找到正确的平衡。

[ISUX转译]iOS 8人机界面指南(二):设计策略

当对应用的交互行为和控件都较为熟悉和可预期时,用户会觉得应用更易上手。那些简单直白的交互行为更容易被用户所理解和记住。

人们会希望在一个操作被执行之前有足够的机会来取消,也希望在执行一个不可逆的操作之前可以有机会来进行确认。最后,人们还会希望能够停止正在执行中的操作。

2.2 从概念到产品(From Concept to Product)

2.2.1 定义应用(Define Your App)

应用的定义是对应用主要功能和目标用户的简明具体的描述。

尽可能早地创建应用的定义可以帮助你将一个想法和功能清单转换为用户想要的条理清晰的产品。在开发过程中,可以使用定义来决定某些功能和行为是否合理。使用以下几个步骤来创建一个可靠的应用定义。

1、列出所有你认为用户可能喜欢的功能

可以直接进行头脑风暴。此时,你需要列出所有与产品中心有关的任务。不用担心清单太长,因为接下来会进行删减。

假设你一开始的想法是开发一个帮助人们购买食品杂货的应用。你可以思考在进行这项活动时,会涉及到哪些相关的任务,这些就是用户可能感兴趣的潜在功能。例如:

  • 创建清单
  • 查找食谱
  • 比较价格
  • 定位商店
  • 给食谱做注释
  • 查找可使用的优惠劵
  • 查看烹饪演示
  • 探索不同的烹调方法
  • 寻找某些食材的替代物

2、确定目标用户

现在你需要清楚地将你应用的用户与其他iOS应用的用户区分开来。确定在此情此景下,什么是对你的用户最重要的。在食品杂货例子中,你可能需要问问你的用户:

  • 通常是在家里做饭还是更喜欢现成的食物
  • 是忠实的优惠券用户还是认为优惠券没多大价值
  • 喜欢寻找特别的食材还是喜欢基本食材
  • 严格按照食谱做菜还是只把食谱当做灵感来源
  • 喜欢少量多次购买还是一次性购买大量食物
  • 希望能保留多个不同目的的清单还是只希望记录回家路上需要购买的几个东西
  • 坚持使用固定的品牌还是会使用方便的替代品
  • 习惯于购买固定的一些物品还是会按照食谱来购买

思考过这些问题之后,你可以从三个方面来描述目标用户的特征:喜欢按照食谱进行尝试,时常很匆忙,通常情况下很节俭。

3、根据目标用户过滤功能清单

如果在确定了一些用户特征后,你最终只得到了几个主要功能,那么你就已经步入了正轨:出色的iOS应用应该聚焦在帮助用户解决问题的核心任务上。

例如,即使第一步想出的那些可能需要的功能都是有用的,也不可能全都被第二步定义的目标用户认可。

当你在目标用户的使用情境下检查功能清单时,就可以判断你的应用应该聚焦在三个主要功能上:创建清单,获得并使用优惠劵,获得食谱。

此时你就可以给出应用定义了,总结该应用做什么以及为谁做。食品杂货购买应用的定义可能如下:

“为热爱烹饪且节俭的用户订制的创建购物清单工具。”

4、不止于此

应用定义应该贯穿于整个开发过程,使用应用定义来确定功能,控件,措辞的合理性。例如:

当你想要新增一个功能时,问问你自己这对应用的主要目的和目标用户是否非常重要。如果不是,可以置之不理。例如,你已经确定了你的用户对大胆新颖的烹饪方法感兴趣,那么着重展示盒装蛋糕和现成的食物就不太合适。

当你考虑用户界面的外观和行为时,问问你自己你的用户更喜欢简单的、流线型的风格,还是有明显主题的风格。以用户目标为指导来完成你的应用,理解用户期望通过你的应用完成什么,例如完成任务的能力,快速找到答案的能力,深入综合内容的能力,或者获得娱乐的能力。例如,尽管你的食品杂货清单应用需要易于理解和快速上手,但你的用户还是可能倾向于一个有关食物的主题界面。

当你考虑应该使用怎样的措辞时,努力和用户对这个主题的专业程度相匹配。例如,尽管你的用户可能不是由专业的大厨组成,但你也可以肯定他们希望看到有关食材和技术专用的措辞。

2.2.2 为任务量身订制界面(Tailor Customization to the Task)

好的iOS应用会根据清晰的目标和易用性来平衡用户界面的设计。为了达到这种平衡,要确保在设计阶段前期就考虑定制化。因为考虑品牌性、原创性和适销性通常会影响定制化的决策,所以专注于定制化怎样影响用户体验是有挑战性的。

可以由应用中的任务着手:用户执行这些任务的频率如何,在什么样的环境下进行?

举个例子,想象一个计算器应用使用的是精心设计的,充满艺术感的风格,并且使用了创新的布局来展示大家熟悉的计算器元素。这些像艺术品一样的细节渲染和创新的布局并不会影响用户去理解怎样点击按钮和查看计算结果。但是对于只想简单完成任务的用户,这种新奇的体验和美丽的界面很快就会失去效用,并且可能成为一种妨碍。

[ISUX转译]iOS 8人机界面指南(二):设计策略

相反,GarageBand(随身录音室应用)可以不展示好看的、逼真的乐器来帮助用户制作音乐,但这样会使应用缺少身临其境的愉悦感。在GarageBand里,界面不只是向用户展示了如何使用,同样使得制作音乐的主任务更容易完成。

[ISUX转译]iOS 8人机界面指南(二):设计策略

当你思考定制化如何增强或减弱用户完成任务的注意力时,记住以下几点:

定制总要有缘由。理想情况下,定制化的用户界面能促进用户完成任务并增强他们的体验。你最好尽可能地用任务驱动定制化决策。

尽量避免增加用户的认知负担。用户对标准界面元素的外观和行为都已经很熟悉了,所以他们不用停下来思考如何使用它们。当用户面对外观和行为与标准不同的元素时,他们就失去了经验优势。除非你的独一无二的元素能够使任务更容易完成,否则用户很可能不喜欢被强制学习一些在其他应用都不通用的操作。

保持内在的一致性。你的应用中自定义元素越多,保持这些元素外观和行为的一致性就越重要。如果用户花费时间去学习了你创建的那些不熟悉的控件,那么他们会希望新学到的这些操作能够在整个应用中通用。

总是以内容为重点。因为标准元素很熟悉,所以它们不会分散用户在内容上的注意力。当你自定义用户界面时,注意确保界面元素不会抢走用户对内容的注意力。例如,如果你的应用允许用户观看视频,你可能选择设计一个自定义的重播控件。但是不管你用的是自定义还是标准的重播控件,都没有它是否在用户开始观看后隐藏,点击屏幕后出现来的重要。

在对标准控件进行重设计时再三思考。如果你不只是想自定义标准控件,而是想重设计,确保你的重设计能提供尽可能多的信息。例如,你设计了一个开关控件,它没有可以指明相反状态存在的信息,那么用户很可能意识不到这是个有两个状态的控件。

一定要彻底测试自定义的界面元素。在测试过程中,近距离的观察用户是否能预测你的元素如何使用以及是否能容易地与它们交互。例如,如果你创建的控件的可点击区域小于44 x 44点,用户点击时就会有困难。或者如果你创建了一个视图对点击和滑动的反馈不一样,确保这个视图提供的功能值得用户去额外关注交互的不同。

2.2.3 原型与迭代(Prototype & Iterate)

在你投入工程资源实现设计之前,创建原型来进行用户测试是个很好的主意。即使只有几个同事来帮你测试原型,你也会收获一些关于应用功能和用户体验的新鲜观点。

在设计的早期阶段,你可以使用纸面原型或者线框图去呈现主要的视图和控件,并且标明每个页面之间的跳转关系。你可以从线框图测试中获得一些有用的反馈,但是线框图的稀疏性有可能会误导用户。因为用户很难想象当线框被实际内容填满时体验会有什么样的变化。

如果你有一个可以在设备上运行的原型,那你可以得到更多有用的反馈。当用户能在设备上与你的原型进行交互时,他们能更容易发现应用中哪里功能不满足预期,哪里体验过于复杂。

创建可靠原型的最简单的方法是使用基于故事板(storyboard)的Xcode模板创建一个基础应用,然后使用一些类似于占位符的内容来进行填充。(故事板(storyboard)文件可以涵盖应用中的所有界面,并且包括界面之间的跳转关系。)接着,将这个原型导入到设备中,这样被测者就可以有一个尽可能真实的体验了。

你不需要在原型中提供大量的实际内容或者使每一个控件都可用,但是你确实需要营造足够的情境来保证真实的体验。并且需要在典型用户体验和非典型的边缘情况之间做好平衡。例如,如果你的应用需要处理很长的列表项,你的原型就不能只显示一两个条目。而且在用户测试交互中,只要被测者能够点击屏幕上的一个区域进入到下一个逻辑页面或者完成主任务,那他们就可能会提供更有建设性的反馈。

当你使用Xcode应用模板来创建原型时,你可以免费使用很多功能,并且它可以相对容易地进行设计中的响应反馈调节。在你确定设计方案并投入资源进行实现之前,应该对原型进行多次迭代测试。想要开始学习Xcode,请参考

Xcode Overview

2.3 案例学习:从桌面到iOS(Case Study: From Desktop to iOS)

2.3.1 iPad版Keynote应用(Keynote on iPad)

桌面版的Keynote 应用是一个十分强大而又灵活的应用,可以创建非常优秀的幻灯片。人们喜爱Keynote将简单易用与良好的细粒度的操作结合进而控制无数精确细节的方式,如动画和文本属性等。

[ISUX转译]iOS 8人机界面指南(二):设计策略

iPad版的Keynote提取了Keynote桌面版的核心要素,并通过创造以下的用户体验使它在iPad上更舒适:

  • 专注于用户内容
  • 在不削减功能的基础上减少系统的复杂性
  • 提供有用而又令人愉悦的快捷操作
  • 延续桌面版本的体验
  • 利用动人的动画提供良好的反馈与交流

Keynote用户能很快理解如何使用iPad版,是因为它使用了iPad原生的范例,符合了用户对功能上的预期。新用户可以用简单、自然的方式直接操控内容,所以可以很容易学会如何使用iPad版的Keynote。

Keynote从桌面版向iPad版的转变是基于从细节到深层的大量修改和重新设计的。这些都是一些最明显的适应性:

流线型的工具栏

工具栏中只有少数的元素,但是它们是用户在创建内容时所使用的全部功能和工具的统一入口。

[ISUX转译]iOS 8人机界面指南(二):设计策略

简化并优先响应用户焦点的检查器

iPad版的Keynote能自动侦测各种工具,能通过对用户需求进行分类以修正被选择的对象。(译者注:特别是根据当前的操作对象而有限选择某些工具。)通常,人们可以在第一检查器视图中完成他们需要的所有修改操作。如果他们需要修改那些不常用的属性,他们可以下拉另一个检查器视图来进行。

[ISUX转译]iOS 8人机界面指南(二):设计策略

丰富的预设样式集

人们可以利用预设的样式很简单地改变对象(如表格或图表)的外观或者感觉。除了颜色之外,每个集中,例如表格的标题和轴区分标识等的预设属性都被设计得与整体的主题和谐一致。

[ISUX转译]iOS 8人机界面指南(二):设计策略

直接操作内容,丰富有意义的动画

在iPad版的Keynote中,用户可以拖动滑块到一个新的位置,可以扭动旋转一个对象,也可以轻击图片来选中它。iPad版Keynote的响应动画进一步加强了这种可直接操作的印象。例如,用户在移动某个滑块时它通常会暂停,而当它被放置在一个新的位置时,环绕在周围的滑块将会向外扩散给它留出空间。

2.3.2 iPhone版邮件应用(Mail on iPhone)

邮件应用是OS X中一款好用而又广受好评的常见应用。它也是一个很强大的程序,可以允许用户撰写、接收、分类和存储邮件,追踪行动和事件,也可以编写备忘录和邀请等。桌面版的邮件应用通过一系列的窗口提供了这些强大的功能。

[ISUX转译]iOS 8人机界面指南(二):设计策略

iPhone版的邮件专注于桌面版邮件的核心功能,帮助人们接收、撰写、发送和组织他们的信息。为了塑造移动iPhone版的邮件应用,将这些功能浓缩在为其量身定制的界面之中,做了如下的工作:

  • 将人们的内容前置和居中的合理化呈现
  • 专为处理不同任务而设计的不同视图
  • 易于浏览并符合认知的信息结构
  • 适时提供强大的编辑和组织性工具
  • 传达动作和提供反馈的微妙且动人的动画

(我们)必须明白相较于桌面版的邮件应用,iPhone版的邮件应用不是(译者注:或者说并不需要是)一个更好的应用,而是为移动端用户重新设计的邮件应用。iPhone版的邮件应用专注于桌面版的功能子集并将它们呈现在一个吸引人的精简界面之中,据此为移动端的用户提供了核心的邮件体验。

为了使邮件应用的体验能适应移动场景,iPhone版的邮件应用在几个关键的方面革新了用户界面。

直接、高度专注的页面

每个页面显示了邮件应用体验的一个方面:账户列表、邮箱列表、消息列表、消息查看和编辑视图。用户可以在一个屏幕内滑动查看完整的内容。

[ISUX转译]iOS 8人机界面指南(二):设计策略

简单、可预见的导航

通过每屏的一次点击,用户可以逐层展开通用内容(账户列表)进入具体页面(一封消息)。每个页面会显示一个标题用以指示用户所在的位置,以及一个返回按钮用以更容易地回溯到他们之前的步骤。

需要时即可获取的、简单的点击性控件

基本上在任何场景之下,编写邮件和查阅新邮件都是人们首要希望进行的操作,因此iPhone版的邮件应用保证了这两个功能在多个页面中都可以便利地进行。当用户查看一封消息时,就会显示诸如回复、移动和删除等对消息的操作。

针对不同任务的不同类型的反馈

当人们删除一封消息时,它会动态地进入垃圾桶图标中。当人们发送一封消息时,可以看到它的发送过程;而当发送结束时,人们可以听到一个特别的声音提示。通过消息列表页面工具栏的副标题,用户通过简单一瞥就可以查看邮箱上次更新的时间。

2.3.3 iOS系统内的网页内容(Web Content in iOS)

iOS版的Safari应用在iOS设备上提供了出众的移动网页浏览体验。人们喜欢阅读清晰的文字和图片,也希望能通过旋转设备或者捏合和点击屏幕来调整视图。

基于标准建立的网站可以在iOS设备上显示得很好。特别是那些能侦测设备并不需要插件的网站可以同时在iPhone和iPad上都表现得很好,两者之间不会需要太多的修改,即使有也很小。

除此之外,成功的网站应具备以下的典型性:

  • 如果页面宽度需要匹配设备宽度,可以设置合适的视窗(viewport)来适应设备
  • 避免CSS中固定的定位,以便当用户缩放或拖动页面时内容无法被移出屏幕
  • 拥有一套基于触控操作的用户界面,而不是依赖基于传统点击操作的交互

有时候,额外的一些修改可以(使页面)更合理。例如,在iOS系统中,很多网页应用会设置合适的视窗(viewport)宽度并通常隐藏Safari的UI。如欲了解更多如何进行这些修改,参见

Safari Web Content Guide

章节中的Configuring the ViewportConfiguring Web Applications

网站也可以通过其他的方法适配桌面网页体验到iOS端的Safari浏览器中:

使键盘适应iOS端的Safari

当键盘和格式辅助信息出现时,iPhone上的Safari应用会将你的网页显示在URL地址下方和键盘与格式辅助信息上方。

使弹出式菜单适应iOS端的Safari

在桌面版的Safari应用中,弹出式菜单会包含很多选项,就如在其他OS X应用中一样。在必要的情况下,菜单展开后可以超出应用窗口的边界以显示其中的所有选项。在iOS版的Safari应用中,弹出式菜单由原生的元素所呈现,这样能提供更好的用户体验。例如,在iPhone上,弹出式菜单会出现在选择器(picker)当中,选择器里会一个用户可选择的选项列表。(欲了解更多选择器控件的内容,可以参见Picker。)

 

英文原文访问地址:iOS Human Interface Guidelines (Design Strategies)

相关文章:

iOS7人机界面指南-UI元素(上)

iOS7人机界面指南-UI元素(下)

iOS7人机界面指南-UI元素(上)

原文转自:腾讯ISUX  (http://isux.tencent.com/ios-human-interface-guidelines-ui-design-ios7-ui-1.html)

译者注:

1. 本次主要翻译了iOS Human Interface Guideline的Part 4-UI Element的前半部分,包括栏(Bars)和内容视图(Content Views)。Part 4-UI Element的下半部分控件(Controls)、临时视图(Temporary Views)以及Part 5将在下次更新中放出。

2. 我们感到非常抱歉,由于苹果文档在不停地变化和更新,本次放出的内容也许不能与最新版文档相匹配,我们会在随后进行修正。

3. 如您在阅读中发现任何疏漏,欢迎您提出宝贵的意见和建议,感谢。

重要:这是一份针对API或其它相关技术开发而准备的预备文档。尽管文档在专业精确程度上已经过多次审查,它仍不是最终版本。文档仅供已注册苹果开发者计划的开发者使用。苹果提供这份文档的目的,是帮助开发者根据文档来规划自身应用的开发技术与界面设计。这些信息将可能发生变化,您的应用也应当根据最新的操作系统与最终文档进行相应的调整。该文档可能会由于API与相关技术的发展而更新版本。

栏(Bars)

状态栏(Status Bar)

状态栏展示了关于设备及其周围环境的重要信息。

1

你可以将状态栏风格设计为全应用统一,或者为应用里不同的视图定义不同的状态栏风格。你可以通过阅读

UIApplication Class Reference

UIViewController Class Reference来分别了解更多关于UIStatusBarStyle常数和preferredStatusBarStyle属性的内容。

外观和行为

状态栏是透明的。不管设备处于横屏还是竖屏,状态栏始终固定在整个屏幕的上边缘,承载用户所需要的如网络连接,时间,电量等信息。

指南

尽管你不会像使用其它UI元素一样编辑状态栏,理解它在应用中的功能仍然很重要。

隐藏状态栏时请慎重。由于状态栏是透明的,通常情况下不需要隐藏它。始终隐藏状态栏意味着用户必须退出你的应用才能知道现在的时间,或者了解是否当前环境下是否有Wi-Fi连接。

在用户全屏观看媒体时,考虑隐藏状态栏以及所有页面UI。当你这么做的时候,请确保用户在轻击屏幕时即可重新唤起状态栏以及相关的UI。而除非你有充分的理由,否则最好不要重新定义一个手势来让用户唤起状态栏,因为用户不会发现,就算发现了也难以记住。

不要创建自定义状态栏。用户依赖系统默认状态栏的一致性。就算你在应用中隐藏了它,也优于定制一个新的UI来代替它。

为你的应用选择配色协调的状态栏颜色。内容是深色的状态栏的在浅色应用中效果出色,而相应的浅色状态栏则更适用于颜色较深的应用。

千万千万,避免在状态栏后面叠加会分散注意力的内容。尤其是,你不能让用户觉得轻击状态栏之后可以获取内容或激活你的应用中的控件。

在适当的时候展示网络活动指示器(network activity indicator)。这可以提醒用户当前远程网络连接(lengthy network access)正在工作。更多详情请参考本章第三节控件(Control)部分的Network Activity Indicator.

导航栏(Navigation Bar)

导航栏能够实现在应用不同信息层级结构间的导航,有时候也可用于管理当前屏幕内容。

2

导航栏包含在导航控制器(navigation controller)中,该控制器是一个用于管理自定义视图中信息层级展示形式的编程对象。想要了解如何在代码中定义你的导航栏内容,请参考Navigation ControllersNavigation Bars.

外观和行为

导航栏通常位于屏幕的上方,状态栏正下方。导航栏居中展示当前屏幕或当前视图的标题。当用户在信息层级中穿梭时,也可以通过点击导航栏中的返回按钮,或轻扫屏幕的边缘来回到上一层。另外,用户可以使用导航栏上相应的控件来管理当前的屏幕内容。

导航栏是半透明的,上面所有的控件都是无边界的(borderless)。

在iPhone上,导航栏始终与屏幕等宽以通栏显示,当用户改变屏幕方向时,导航栏的高度也将自动发生改变。

指南

你可以用导航栏在不同视图间提供导航,或在上面放置管理当前视图内容的相关控件。

使用当前视图的标题作为导航栏标题。若觉得标题冗余,你也可以将标题留空。

当用户到达一个新的层级,你需要做以下两件事:

  • 将导航栏标题改为当前层级的标题
  • 在当前标题左侧放置返回按钮,按钮的标题应为前一层级的标题

确保导航栏上的文字容易阅读。系统默认字体的可读性最好,但合适的话,你也可以使用其它的字体。

考虑在应用最高层级的导航栏中放置一个分段控件,它能够帮助你更好地扁平信息层级,也会让用户更容易找到所需内容。如果在导航栏中使用了分段控件,请确保返回按钮标题命名的准确。更多详情请参考本章第三节中的Segmented Control。

即使空间充足,也应当避免让过多的控件填满你的导航栏。导航栏上应该不多于以下三个元素:当前视图的标题、返回按钮和一个针对当前的操作控件。相反的,当你在导航栏中使用了分段控件,就不要再放标题以及其它多余控件了。

根据控件的标准含义来选择系统提供的按钮。详情请参考下文中工具栏与导航栏标准按钮(Toolbar and Navigation Bar Buttons).如果想自定义导航栏控件,请参考文档第五章中Bar Buttons Icons给出的建议。

根据你应用的配色来定义你的导航栏颜色。举个例子,你可以为导航栏自定义背景图片,也可以指定它的色调与透明度。有时候使用可缩放的背景图(resizable background image)是个好主意。想要了解更多可缩放背景图的使用方法,请参考文档第五章Creating Resizable Images.请提供适用于iOS 7应用的图片高度,更多详情请参考iOS 7 UI Transation Guide中的Navigation Bar部分。

确保你的导航栏与你的应用的外观和风格是协调的。举个例子,不要在同一个应用中使用不透明导航栏和半透明工具栏。在屏幕处于同一方向时,最好不要改变不同屏上导航栏的背景图片、颜色和透明度。

确保你自定义的返回按钮长得像返回按钮。用户知道系统默认的返回按钮能帮助他们在信息层级中追踪自己的路径,如果你想重新设计它,请仍然使用自定义模版图片(custom mask image),它可以在iOS7中让这些按钮标题在系统各转场中出现或者消失。

在iPhone上,要考虑到由于屏幕方向的变化将会导致导航栏高度自动变化。确保你自定义的图标可以适应导航栏高度变小的情况。不要将导航栏高度写死,可以利用UIBarMetrics常数来确保图标的适应性。

工具栏(Toolbar)

工具栏上放置着用于操作当前屏幕中各对象的控件。

3-工具栏下方

工具栏包含在导航控制器(navigation controller)中,该控制器用于管理定制视图中信息层级的展示形式。 想要了解如何在代码中定义工具栏,请参考View Controller Catalog for iOS中的Displaying a Navigation ToolbarToolbar部分。

外观和行为

在iPhone上,工具栏始终位于屏幕底部,在iPad上则有可能出现在顶部。

工具栏是半透明的,栏中各项以等距方式排列。因为不同界面所对应的操作不同,工具栏中的控件可能随着界面的切换而进行相应调整。

在iPhone上,当用户从竖屏转换为横屏时,状态栏的高度将自动发生改变。在iPad上,工具栏的高度则不会因设备方向而发生变化。

指南

你可以在工具栏上放置可让用户对当前视图内容进行操作的工具。

在工具栏里放置用户在当前情景下最常用的指令。你也可以在工具栏里放置分段控件以方便用户快速到达不同视图或模式;更多使用指南,请参考本章第三节——控件(Controls)中的分段控件(Segmented Control)。

如果需要在工具栏上展示3个以上的项目,可以使用图标。由于文本按钮通常会比图标更占空间,所以用图标可以避免文字标题们挤在一起。

保证工具栏标题按钮之间有足够的间距。控件过于拥挤会让用户觉得它们难以区分。如果工具栏中按钮的标题看起来太接近,可以用UIBarButtonSystemItemFixedSpace常数来增加他们之间的间距。(更多关于这个常数的内容可以参考

UIBarButtonItem Class Reference



在iPhone上,要考虑到由于屏幕方向的变化将会导致工具栏高度的自动变化。确保你自定义的控件可以适应横屏模式下工具栏高度变小的情况。不要将工具栏高度写死,可以参考UIBarMetrics 常数来确保工具栏中各项的适应性。

工具栏与导航栏标准按钮(Toolbar and Navigation Bar Buttons)

iOS提供了一系列用于工具栏与导航栏的标准按钮。想要了解如何设计自定义图标,请参考文档第五章Bar Button Icons部分。工具栏和导航栏图标的颜色可以通过tintColor属性来设定。

想要了解每一个按钮所对应的标志名称及其含义,请参阅

UIBarButtonItem Class Reference

中的UIBarButtonSystemItem部分。

重要:跟所有标准按钮和图标相同,应当根据文档中说明的图标含义,而不是只凭图标外观来使用这些工具栏图标和导航栏图标。这样能够保证在关联特定意义的按钮改变了外观的情况下,你的应用中的UI仍然是可用而有意义的。

4-toolbar buttons

除了以上展示的标准按钮之外,你还可以使用系统提供的编辑、取消、保存、完成、撤销、重做等等按钮来支持编辑或其它操作。这些按钮的标题即是按钮的操作内容。想要了解每一个按钮的名称及其含义,请参阅

UIBarButtonItem Class Reference

中的UIBarButtonSystemItem.

另外,你还可以在工具栏中放置系统提供的信息按钮(info button).

info button

标签栏(Tab Bar)

标签栏用于让用户在不同的子任务、视图和模式中进行切换。

5-tab bars

标签栏包含在标签栏控制器中,该控制器用于管理自定义视图的展示形式。想要了解如何在代码中定义标签栏,请参考Tab Bar ControllersTab Bars.

外观和行为

标签栏位于屏幕底部,并应该保证在应用内任何位置都可用。标签栏是半透明的,展示图标和文字内容,每一项均保持等宽。当用户选中某个标签时,该标签呈现适当的高亮状态。

标签栏是半透明的,始终与屏幕等宽以通栏显示。因为不同界面所对应的操作不不同,工具栏中的控件可能随着界面的切换而进行相应调整。

在iPhone上,一个标签栏一次最多可承载5个标签;多于5个标签的时候,可以展示前4个标签,把剩余的标签以列表的形式收在 “更多”之中。iPad的标签栏则可以承载5个以上的标签。

你可以在标签上加上红底白字,显示数字或者省略号的小气泡(badge),用以展示与应用相关的信息。

标签栏的高度不随着屏幕方向的改变而改变。

指南

你可以使用标签栏来切换对同一组数据的不同视图模式,或者整体功能下不同的子任务。当你使用标签栏时,请遵守以下指南:

一般而言,使用标签栏来组织整个应用层面的信息结构。标签栏非常适合用于应用的主界面中,因为它可以很好地扁平信息层级,并且同时提供多个进入不同信息种类的入口。

不要使用标签来执行对于当前屏幕内容的操作。如果你需要给用户提供操作控件,请使用工具栏。

即使标签当前不可用,也不要把它从标签栏中删除。如果某个标签所代表的部分功能在当前场景下不可用,可以将它标识为不可用状态,但不要删除它。让某些标签时而出现时而隐藏,会让用户觉得应用UI不稳定而且难以预测。最好的解决方式是确保每个标签都可用,并解释当前标签不可用的原因。举个例子,当用户没有在设备中保存任何歌曲,在系统音乐应用的歌曲标签页里就可以教育用户如何去下载一首歌。

考虑在tab上加入红色的小气泡(Badge)以传达信息。你可以通过添加小气泡来告知用户该标签中包含新的内容。

根据控件的标准含义来选择系统提供的图标。详情请查看下文中的标签栏标准图标(Tab Bar Icons)。如果想自定义标签栏图标,请参考文档第五章中Bar Buttons Icons里给出的建议。

可能的话,自定义你的标签栏外观。举个例子,只要你的图标是系统默认的标准图标或遵循了系统模版的图标,你都可以为你的标签栏以及上面的图标设计特有的视觉风格。有时候使用可缩放的背景图是个好主意,想要了解更多可缩放背景图的使用方法,请参考文档第五章Creating Resizable Images.

在iPad上,你可能会在对分视图(split view pane)或者浮出层(popover)内使用标签栏以切换或筛选视图中的内容。然而通常情况下,在对分视图和浮出层底部使用分段控件效果会更好,因为视觉上看起来更为协调。更多详情请参考文档本章第三节——控件(Controls)中的Segmented Control.

在iPad上,避免让过多的标签填满你的标签栏。放置太多控件将导致用户难以点击,同时每添加一个标签,意味着你的应用程序又复杂了一分。一般来说,在主界面(main view)和对分视图的右窗格上来说,标签数量应该控制在7个左右;而对于浮出层和对分视图的左窗格来说,标签数以5个左右为宜。

在iPad上,无论横屏还是竖屏情况下都应展示相同数量的标签,以提高应用的视觉稳定性。竖屏视图中我们推荐标签个数在7个左右,在横屏中,你应该将相同数量的标签居中展示。这个建议同样适用于在对分视图或者浮出层中的标签栏。举个例子,如果你在竖屏模式下的一个浮层中使用了标签栏,那么横屏时它应该也能很好地展现在对分视图的左窗格中。

 标签栏标准图标(Tab Bar Icons)

iOS提供了一系列标签栏标准图标。想要了解如何设计自定义图标,请参考文档第五章Bar Button Icons部分。标签栏图标的颜色可以通过tintColor属性来设定。

想要了解每一个图标的名称及其含义,请参阅

UIBarItem Class Reference

中的UIBarButtonSystemItem部分。

重要:跟所有标准按钮和图标相同,应当根据文档中说明的图标含义,而不是只凭图标外观来使用这些图标。这样能够保证在关联特定意义的按钮改变了外观的情况下,你的应用中的UI仍然是可用而有意义的。

6-table 34-2

搜索栏(Search Bar)

搜索栏获取用户键入的文本,用以作为搜索的关键字(下图中显示的文本为占位符,非用户输入文本)。

6-search bar

想要了解更多的详情,请参考Search Bars

外观和行为

搜索栏的外观类似文本框。默认状态下左侧有搜索图标,当用户点击搜索栏时会自动出现键盘;用户输入完后,系统将按照应用程序定义的方式来处理输入的文本。

搜索栏可能包含以下这些可选元素:

  • 占位符文本(Placeholder text)。占位符文本通常会写明控件的功能——如 “搜索”,或者提示用户输入的文本将在哪里搜索,如“Google”。
  • 书签按钮(The Bookmarks button)。书签按钮可以让用户方便地找到他们需要的内容。例如在地图中搜索时,用户可以通过书签按钮快速选中书签地址、最近搜索记录、或联系人。书签按钮只有当搜索栏中没有占位符或用户输入内容时才会出现,当搜索栏中已有文本时,书签按钮会被清楚按钮(Clear button)所代替。
  • 清除按钮(The Clear button)。大多数搜索栏都会提供清除按钮,方便用户一键清空输入内容。一旦用户在文本框中输入内容,清空按钮就会出现;而当搜索框中没有任何文本内容时,清空按钮将被隐藏。
  • 结果列表图标(The results list icon)。结果图标说明此次搜索有搜出结果。当用户点击它时会出现用户最近一次搜索的搜索结果。
  • 描述性标题,我们称为提示 (Prompt)。描述性标题通常放在搜索栏上方。举例来说,它可以是一个为搜索栏提供指引信息的短语。

指南

在你的应用中使用搜索栏而非文本框来让用户进行搜索。因为文本框的外观不符合用户对搜索的预期。

你可以为搜索栏指定颜色或设计背景图片。在iOS 7里,你也可以把搜索栏放在导航栏中。请参考UISearchDisplayController

如果你决定在搜索栏里使用背景图片,最好采用可调整尺寸的背景图,想要了解更多可调整尺寸背景图的使用方法,请参考文档第五章中Creating Resizable Images的内容。

范围栏(Scope Bar)

范围栏只有在与搜索栏一起时才会出现(通常出现在搜索栏下方),它让用户可以定义搜索的范围。

7-scope bar

想要了解如何在代码中定义搜索栏与范围栏,请参Search Bar

外观和行为

当搜索栏出现时,范围栏会出现在它的附近。范围栏的外观与你所指定的搜索栏的外观兼容。

指南

当用户想在明确的分类范围内进行搜索时,使用范围栏是非常有用的。然而,更好的选择是优化您的搜索结果,让用户不需要使用范围栏对搜索结果进行筛选,便可以找到他们所需要的内容。

 

内容视图(Content Views)

活动菜单(Activity)

每个活动菜单表示一个系统提供的服务或定制服务——它可以通过访问活动视图控制器(Activity view controller)来作用于某些特定的内容。

8-activity

想要了解如何在代码中定义标签栏,请参考

UI Activity Class Reference

.

外观和行为

活动菜单指的是指一种代表当前应用所支持服务的对象。比如说,当用户点击分享按钮,应用程序将呈现活动视图控制器来告诉用户当前可以进行什么操作。想要了解如何将活动视图控制器整合进应用中,请参考Activity View Controller.

每个活动菜单都会以一个图标加图标下方文字的形式呈现。系统提供的活动菜单可以使用以下两种图标风格的任意一种:看起来更像一个应用图标的,或是看起来像工具栏标准图标,而第三方的活动菜单图标通常会选择后一种。这两种风格可以从下面的活动视图控制器中看到。

activity

用户通常通过点击控制器中的活动图标来启动某样活动。点击之后该项服务通常会立刻执行,除非这项服务过于复杂,此时系统将会进一步索取更多的信息之后才会为用户执行该服务。

指南

使用活动菜单来让用户执行你的应用所提供的服务。请注意,iOS本身提供了若干内置的服务,如打印,转发到Twitter,发送信息和Airplay等等,你不需要再另外创建它们。

为你应用的各种服务设计一套精简的模版图标(Template image)。如果想制作出好看的模版图标,设计的时候可以遵循以下原则:

  • 使用透明度适当的黑色或白色
  • 不要使用阴影
  • 进行抗锯齿处理

一个活动模版图大小应该保持在70×70像素左右(高分辨率下),在区域里居中显示。

为每一个活动菜单设计清晰简练的文字标题。标题将会出现在活动菜单图标的下方。短标题通常效果最好,因为它在屏幕上的显示效果更好并且更容易本地化。如果你的标题文字过长,iOS会将缩小文本,仍然过长的话则会被截断。一般而言,最好能避免在活动标题中提及你的公司或产品名称。

活动视图控制器(Activity View Controller)

活动视图控制器是一个临时视图,当中罗列了一系列可以针对页面特定内容的系统服务和自定义服务。

10-activity view controller

想要了解如何在代码中定义活动视图控制器,请参考

UIActivityView Class Reference

.

外观和行为

活动视图控制器中列出了让用户操作当前内容的一系列可配置的服务。用户可以通过轻击分享按钮来查看活动视图控制器的内容。

活动视图控制器需要配合一系列的活动菜单来使用,每个活动菜单代表了一个特定的服务。想要了解如何设计一个自定义活动菜单,请查看上文的活动菜单(Activity).

在iPhone和iPod Touch上,活动视图控制器在操作列表(action sheet)中出现;在iPad上则会出现在浮出层(popover)中。

指南

使用活动视图控制器来为用户提供一系列针对当前内容的服务。这些服务可以是系统自带的,比如复制,分享到twitter,打印等等,也可以是自定义的。活动视图控制器通常用作让用户把他们选中的内容复制到他们的社交媒体账户上。

不要创建一个自定义按钮来触发活动视图控制器。用户更习惯点击分享按钮后使用系统提供的服务。你应该学会如何更好地利用用户这一既定习惯,而不是强迫他们以一种全新的方式来完成同样的事情。

确保控制器中的操作适用于当前场景。你可以适当地在活动视图控制器中增减系统操作,或增加自定义操作。例如,如果你不希望用户打印某张图片,你可以把打印功能从控制器中删除。

注意:你不能改变系统默认服务在控制器中的顺序。同时,所有系统服务都应出现在自定义服务之前。

集合视图(Collection View)

集合视图用于管理一系列有序的项,并以一种自定义的布局来呈现它们。

12-collection view

想要了解如何在代码中定义集合视图,请参考Collection View Programming Guide for iOS.

外观和行为

集合视图是可以自定义的滚动视图,视图中罗列了用户可以对内容进行操作的一系列项。用户用手势来与视图中的各项进行交互,也可以进行导入、移动和删除等操作。

集合视图的整体布局与其中各项的外观样式是由集合视图与若干其它对象的代码共同定义的。这些对象中,布局对象(layout object)居于首位,它是UICollectionViewLayout的标准或自定义子类,定义了这些项的具体位置以及视觉属性。为了方便使用,UIKit提供了UICollectionViewLayout object,它为一组网格排列的项定义了可调整的线性排序。

在集合视图中,可选补充视图(optional supplementary views)可以在视觉上区分各项的子集。集合视图也支持装饰视图,也就是说你可以自定义它的背景和外观。

当用户在集合视图中导入、移动或者删除项的时候,会出现系统默认的动画效果。集合视图同样支持开发者额外定义手势识别来执行自定义操作。默认情况下,集合视图可以识别轻击(tap)某项以选中,和长按(touch-and-hold)某项进行编辑。

在iOS 7里,集合视图支持开发者自定义各个布局间的转场动画。更多的详情可以参考UICollectionViewTransitionLayout Class Reference.

指南

使用集合视图来让用户查看和操作一系列不适合以列表形式呈现的项。由于集合视图的布局不是一个严格的线性布局,因此尤其适合用来展示一些尺寸不一致的项。

集合视图支持广泛的自定义,因此我们要尽量避免把心思都放在进行全新的设计上。集合视图是用来帮助用户更好地完成任务的,视图本身并不是用户体验的焦点所在。

以下指南可以帮助你设计出用户体验更好的集合视图:

  • 可以使用表格视图(Table View)的时候,不要使用集合视图。有时候用户会觉得以列表呈现的信息更容易阅读和理解,例如将文本信息放在滚动列表中滚动列表中的时候,用户阅读和处理起来会更为简单和高效。
  • 让视图中的项更容易点击。如果用户很难点中集合视图中的项,他们是不会愿意用你的应用的。跟所有用户可以点击的UI对象一样,请确保你的集合视图中每一个项的最小点击区域有44×44pt,尤其是在iPhone上。
  • 当你要让整个布局进行动态变化时,请务必谨慎。集合视图允许你在用户浏览和操作项的时候调整视图的布局。但当你决定调整它的时候,请确保这个动态变化是有意义并且容易理解的。没有明确目的而贸然改变集合视图的布局会让用户对应用留下难用、不符合预期等负面的印象。更有甚者,如果用户此时关注的项在变化中消失了,用户会觉得这个应用超出了他们的控制能力。

容器视图控制器 (Container View Controller)

容器视图控制器采用自定义的方式来管理和呈现它的视图控制器或一系列子视图。系统定义的容器视图控制器包括标签栏视图控制器(Tab bar view controller)、导航视图控制器(navigation view controller)和对分视图控制器(split view controller).

想要了解如何在代码中定义容器视图控制器,请参考UIViewController Class Reference.

外观和行为

容器视图控制器不存在任何预先定义好的外观或者行为。如果你在自定义容器视图控制器对象的时候把UIViewController归为子类,你可以自己规定它里头应该包含多少子类,以及它们将如何展现出来。

指南

用容器视图控制器来呈现内容,用户可以通过控制器来以自定义的方式进行导航。

先问问你自己是不是必须用到容器视图控制器。用户会更习惯诸如对分视图、或者是标签栏视图这类他们所熟知的东西。你必须确保你设计的控制器的优点不会由于用户不熟悉、不认识、不会用而白费功夫。

确保你的容器内容控制器在横屏与竖屏模式都可用。你的容器视图控制器无论在横屏还是竖屏中,体验都应该是一致的。

一般来说,避免太过花哨的转场动画。如果你采用了故事板(storyboard)的设计方法来设计你的视图内容控制器,你往往自然而然地会为它自定义一些动画。但绝大多数情况下,这些花哨的转场动画会让用户分心,让他们忘记了当前要做的事,还可能降低你的应用整体的美感。

图片视图(Image View)

图片视图用以展示一张单独的图片,或者一系列动态图片。

想要了解如何在代码中定义图片视图,请参考image views.

外观和行为

图片视图不存在任何预先定义好的外观,同时在默认状态下它不支持用户的交互行为。图片视图可以检测图片本身及其父视图(parent view)的属性,并决定这个图片是否应该被拉伸、缩放、调整到适合屏幕的大小,或者固定在一个特定的位置。

在iOS 7里,包含了模版图片(template image)的图片视图会把当前的色调(tint color)应用到图片上。

指南

请务必确保图片视图中的每一张图片都保持相同的尺寸和比例。如果你的图片尺寸各不相同,图片视图将会逐一对它们进行调整;而当你的图片比例不一,渲染的时候很可能会出错。

地图视图(Map View)

地图视图呈现地理数据,同时支持系统内置地图应用的大部分功能(如下图所示)。

13-map view

想要了解如何在代码中定义地图视图,请参考

Map Kit Framework Reference

.

外观和行为

地图视图通常以标准地图、卫星图像、或两者结合的形式来展示地理区域。地图样式还会标注单一的地点(annotations),描绘路径和二维区域的轮廓。

用户可以缩放和移动地图视图——除非你在应用中禁用了这些动作——你也可以在编程时自定义地图视图的缩放和移动。

指南

利用地图视图可以给用户提供一个可交互的地理区域视图。如果你在开发一个导航类应用(routing app),可以使用地图视图来展示你给用户的路径。

一般来说,允许用户在视图中进行交互行为。用户习惯了在系统内置地图中进行交互,因此他们会有预期,能在你所提供的地图中进行类似的行为。

使用标准的地图标注颜色。地图上标注了一系列地点。因为用户习惯了内置地图的各个标注的颜色,所以最好避免在你的应用中重新定义这些颜色的含义。定义颜色时,请遵循以下这些标准:

红色——表示目的地

绿色——表示起点

紫色——表示用户指定的地点(User-Specified Point)

页面视图控制器(Page View Controller)

页面视图控制器通过滚动(Scrolling)或翻页 (Page-curl transition style)来处理长度超过一页的内容。

14-page view controller

想要了解如何在代码中定义页面视图控制器,请参考Page View Controller.

外观和行为

带滚动条的页面视图控制器没有默认的外观。带翻页效果的控制器可以在两页中间增加书脊(book spine)的效果——当用户翻页时,它看起来就像一本真实的书在翻页一样。

页面内容控制器可以根据指定的切换效果来模拟出页面切换时的动画。使用滚动条效果的时候,当前页面将滚动到下一页;而使用翻页效果时,页面上会出现一个模拟实体书或笔记本翻页效果的翻页动画。

指南

使用页面视图控制器来展示那些线性的内容——例如一个故事的文本,或者是一些可以被自然地拆分成块的内容——比如说,日历。

页面视图控制器让用户从一页移动到前一页或者后一页,而并不支持用户在并不相邻的页面间快速切换。如果你希望在页面视图控制器中展示一些非线性的内容——比如说字典,或者书籍的目录——那么你就需要自定义一种方式,让用户可以随意地到达不同的内容区块。

滚动视图(Scroll View)

滚动视图方便用户浏览尺寸超越滚动视图边界的图片(下图中地球的图片无论是长度还是宽度都超过了)。

想要了解如何在代码中定义滚动视图,请参考“Scroll Views”。

15-scroll view

外观和行为

滚动视图刚出现或者当用户在对它进行操作的时候——纵向和横向的滚动指示器会短暂地闪烁,以提示用户在当前视图外仍有更多内容。跟瞬态滚动条(transient scroll indicator)不一样,滚动视图没有预定义的外观。

滚动视图的响应速度和对各个操作手势的识别都应当让用户感到自然。当用户在视图中拖拽内容,内容随之滚动;当用户轻扫屏幕时,内容将快速滚动——直到用户再次触摸屏幕或内容已经到达底部时停止。滚动视图同样可以应用在页模式(paging mode)中,在此模式下用户可以通过拖拽和轻击等手势来浏览一页的内容。

指南

你可以使用滚动视图来允许用户在固定的空间内浏览大尺寸或大量的视图。因为在iOS中用户常常会用到滚动视图,所以请确保你的应用中滚动视图的操作与他们的预期相同。

适当地支持缩放操作。如果放大和缩小对于当前内容是有用的话,你可以支持用户通过捏或者双击来对当前视图进行缩放。而若是支持了缩放操作的话,你还应当设定在当前情景下允许缩放的最大值和最小值。如果你允许一个字符被放大到充满整个屏幕的话,用户会很难阅读当前内容。

在页模式滚动视图中,可以考虑使用页面控件(page control)。当你想要展示分页、分屏或者分块的内容,最好是让用户知道当前内容一共有多少块,以及他们当前浏览的是第几个——页面控件以圆点的形式来把这个信息告诉用户。同时由于页面控件被广泛用于Safari,股票,天气以及其它系统内置应用中,用户很容易理解它的用途。

当你在滚动视图中使用页面控件的时候,最好禁用同一方向的滚动指示器(scroll indicator)。这样一来用户就有了一种唯一且清晰的方式来浏览当前内容。想要了解更多请参考网络视图(Web View).

一般来说,一次只展示一个滚动视图。如果在一屏中同时存在不止一个滚动视图,由于用户滚动屏幕时动作幅度经常都会很大,他们很容易会碰到另一个。如果你确实要在同屏中放两个滚动视图,可以考虑给他们设定不同的滚动方向,来避免用户想要滚动一个视图的时候误操作。比如iPhone上的股票应用,纵向滚动上半部分会展示股票报价,横向滚动下半部分时则展示该公司的特定信息。

表格视图(Table View)

表格视图以单列多行的形式来展示数据。

16-table view

想要了解如何在代码中定义表格视图,请参考Tabel View Programming Guide for the iOS以及Table Views.

外观和行为

表格视图中的数据是以单列的方式展示的,因此也很容易将它们分段或者分组。用户通过轻击或者拖拽来浏览不同组的信息。用户可以点击来选中某行,通过控件来添加、移除、多选、查看详情或者展开另一个表格视图。当用户选中某一行时,该行会短暂地高亮。

当选中某行将展开另外一屏内容的时候,该行会短暂地高亮,然后新一屏内容滑入。当用户回到前一屏时,之前选中的那一行同样会短暂地高亮,提醒用户他们先前选中了什么。

iOS定义了两种表格样式。

平铺型(Plain)表格可被分为若干带标签的段落,表格右侧可能会出现垂直的表格索引。每行开头可以有页眉,尾部可以有页脚(也可以没有)。

17-plain

分组型(Grouped)中至少含有一组列表,而每一组中至少包含一项内容。分组可以有页眉和页脚。与平铺型不同,分组型表格没有索引。

18-grouped

iOS提供了若干表格视图元素(table-view elements)来扩展表格视图的功能。除了特别标明外,这些元素只适用于表格视图。

table35-1

除了以上表格中列举的元素外,iOS定义了刷新控件,让用户可以刷新当前的表格内容。想要了解更多关于刷新控件的用法,可以参考文档本章第三节控件中的Refresh Controls.

iOS定义了在平铺型表格和分组型表格中最常用到的四种单元格布局样式。每种单元格样式都有最适合展示的信息类型。

注意:从编程角度来说,这些样式应用于单元格中,用以控制表格里每一列的绘制方式。

默认型(Default)(UITableViewCellStyleDefault)

默认型样式包括左侧的图标(可选),和图标右边左对齐的文字标题。

默认型样式适合展示一系列无须通过附加信息便可以区分的项。

19-default cell

副标题型(Subtitled)(UITableViewCellStyleSubtitle)

副标题型包括左侧图标(可选),图标右边左对齐展示的文字标题,以及在标题下方同样左对齐展示的副标题。

左对齐的文本标签让用户可以更快速地扫视表格。这种样式适用于列表各项较为相似的情况,用户可以通过副标题中的详细信息来区分列表中的各项。

20-subtitle cell

Value 1 (UITableViewCellStyleValue1)

在Value 1样式下,标题左对齐,副标题用较细的字体右对齐。

21-value 1

Value 2 (UITableViewCellStyleValue2)

在Value 2样式里,蓝色标题右对齐,副标题左对齐,混排在同一行中。这种样式通常不包含图片。

Value 2的布局中,文本和副标题中间的垂直间距会让用户专注于副标题的第一个单词。

22-value 2

注意:以上四种单元格样式均支持添加表格视图元素,如勾选或展开标志。添加这些元素会缩小标题以及副标题单元格的可用宽度。

指南

使用表格视图可以简洁而高效地展示少量或者大量信息。举例来说,你可以通过表格视图来:

展示用户可选的选项列表。你可以使用选中标记来告知用户当前选中了哪些项。

展示层级信息。平铺型表格样式非常适合展示层级信息。表格中的每项都指向不同的子信息,这些子信息承载于另一个列表中。用户可以沿着这些层级结构的路径来点击每一层列表中的项。展开标志告知用户点击这一列中的任何位置,都将展开新的列表以展示其子类信息。

展示可以在概念上进行分组的信息。平铺型和分组型列表都允许你通过提供页眉和页脚来对信息进行分组和分段。

对于iOS 6.0以上的版本,你可以用页眉页脚视图(header-footer view)——即UITableViewHearderFooterView中的一个实例——来展示页眉和页脚的文字,或图片。想要了解如何在代码中定义页眉页脚视图,请参考UITableViewHeaderFooterView Class Reference.

展示索引来方便查找。平铺型列表支持索引,可以让用户快速地找到需要的内容。索引信息包含一个浮在屏幕右侧的、纵向罗列的条目,内容则通常是字母表中的字母。

如果你使用了索引,要避免在表格右侧使用其它表格视图元素——比如展开指示符——因为它们与索引是冲突的。

使用表格视图时可遵循以下指南:

用户选择列表项时,始终给与反馈。当用户点击可选的列表项时会认为被点击的项都应短暂地高亮一下。在点击后,用户期望出现新的视图,或者出现一个复选标记以表明先前点击的项已经被选中或激活。

如果表格的内容庞大而且复杂,不要等数据都加载完之后才一起显示出来。可以首先展示文字信息,图片等较为复杂的内容则在加载完后再显示。这样可以将有用的信息立即传达给用户,同时也提高了应用的响应能力。

在等待信息加载的时候,可以考虑展示“过期”信息。尽管我们并不推荐在数据频繁变化的应用中这样做,它还是可以帮助更多的静态应用程序立即给到用户有用的信息。当然在你这么做之前,请认真衡量你应用中数据的变化频率,并弄清楚你的目标用户有多需要立即获取最新的信息。

如果信息加载速度很慢或者非常复杂,你需要告诉用户加载正在进行中。如果表格中所有内容都很复杂,我们很难即时地给用户展示任何内容。在这种极端情况下,切勿显示空白的表格,因为这会让用户以为应用挂了。此时应当在屏幕中央展示一个活动指示器(activity indicator)和一个信息标签(information label),比如“加载中…”,让用户知道加载仍然在进行。

如果合适的话,为删除按钮自定义一个名称。如果这能让用户更好地理解应用的相关功能的话,你可以创建一个合适的标题,来取代“删除”这个字样。

尽量使用简洁的文字标签,以避免被截断。繁冗的文字和词组不方便用户浏览和理解。以上所有单元格样式均会自动截断文本,而文本截断所造成的问题可大可小,取决于你采用的单元格样式,以及被截断了哪一部分文字。

如果你想以一种非标准的形式来布局你的表格,最好是自定义一种单元格样式,而不是在现有的表格样式上进行改动。如何创建自定义单元格样式,请参考Table View Programming Guide for iOS中的Customizing Cells部分。

文本视图(Text View)

文本视图可以接收和展示多行文本。

23-text view

想了解如何在代码中定义文本视图,参考Text Views.

外观和行为

文本视图是矩形,可定义为任何高度。当内容太多超出视图的边框时,文本视图支持滚动。

如果文本视图支持用户编辑,当用户轻击文本视图内部时,将唤起键盘。键盘的布局和类型取决于用户的系统语言设置。

指南

你可以控制文本视图中文字的字体、颜色和对齐方式。文本视图的默认字体是系统字体,默认字色是黑色。默认文字对齐方式是左对齐(你可以改为居中或右对齐)。

始终确保文字的易读性。虽然你可以使用属性字符串将不同的字体、字色和对齐方式串联在同一个文本视图内,但保持文本的可读性是必不可少的。最好是可以支持动态文本(Dynamic Type)和UIFont method preferredFontForTextStyle来展示文本框中的文本。

根据输入内容的类型来指定不同的键盘类型。举例来说,你希望用户能更方便地输入网址、密码或者电话号码。但请注意,由于键盘的布局以及输入方法是由用户的系统语言设置决定的,这是你不能控制的。iOS提供了各种不同的键盘类型,以便用户输入不同类型的文本。想要了解可用键盘类型,可以参考UIKeyboardType.想要了解如何在管理你的应用中的键盘,请参考iOS App Programming Guide中的Managing the Keyboard部分。

网络视图(Web View)

网络视图是一个可以展示丰富的HTML内容的区域。(下图是iPhone自带的邮件应用,网络视图指的是下图中导航栏和标签栏中间的区域)

24-web views

想要了解如何在代码中定义网络视图,请参考Web Views.

外观和行为

除了展示网络内容外,网络视图还会自动处理页面中的内容,比如把页面中的电话号码转化成电话链接(译者按:phone link,点击之后iPhone将自动拨打该号码)。

指南

如果你经营一个网页或者网络应用,你大约会用网络视图来实现一个简单的iOS App,来对你的网页或者应用进行一个封装。如果你打算用网络视图来访问你所控制的网页内容,请务必阅读Safari Web Content Guide.

不要用网络视图来创建一个看起来像迷你网络浏览器的应用。用户期望使用iOS自带的Safari来浏览网页内容,因此我们并不推荐你在自己的应用中复制这种以被广泛应用的功能。

 

翻译原文下载:iOS  Human Interface Guidelines
中文翻译PDF下载:ios人机界面指南(UI元素(上))ISUX原创翻译

iOS 8人机界面指南(一):UI设计基础

1.1 为iOS而设计(Designing for iOS)

iOS 的革新关键词如下:

  • 遵从:UI能够更好地帮助用户理解内容并与之互动,但却不会分散用户对内容本身的注意力。
  • 清晰:各种大小的文字应该易读,图标应该醒目,去除多余的修饰,突出重点,很好地突显了设计理念。
  • 深度:视觉的层次和生动的交互动作会赋予UI新的活力,不但帮助用户更好理解新UI的操作并让用户在使用过程中感到惊喜。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

无论你是重新设计一个现有的应用或是重新开发一个,请尝试下列方法:

  • 首先,去除了UI元素让应用的核心功能呈现得更加直接并强调其相关性。
  • 其次,直接使用iOS的系统主题让其成为应用的UI,这样能给用户统一的视觉感受。
  • 最后,保证你设计的UI可以适应各种设备和不同操作模式,这样用户可以在不同场景下舒适地享用你的应用。

在整个设计过程中,时刻准备着推翻先例,假设问题,并以重点内容和功能(为目标)来驱动每个细节设计。

1.1.1 以内容为核心(Defer to Content)

虽然明快美观的UI和流畅的动态效果是iOS体验的亮点,但内容始终是iOS的核心。

这里有一些方法,以确保你的设计能够提升你的应用功能体验并关注内容本身。

充分利用整个屏幕。天气应用是最好的例子:漂亮的天气图片充满全屏,呈现用户所在地当前天气情况这最重要的信息,同时也留出空间呈现了每个时段的气温数据。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

尽量减少视觉修饰和拟物化设计的使用。UI面板、渐变和阴影有时会让UI元素显得很厚重,致使抢了内容的风头。应该以内容为核心,让UI成为内容的支撑。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

尝试使用半透明底板。半透明的控件——比如控制中心——关联了使用场景,帮助用户看到更多可用的内容,并可以起到短暂的提示作用。在iOS中,透明的控件只让它遮挡住的地方变得模糊——看上去像蒙着一层米纸一样——它并没有遮挡屏幕剩余的部分。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

1.1.2 保证清晰度(Provide Clarity)

保证清晰度是另一个方法,以确保你的应用中内容始终是核心。以下是几种方法,让最重要的内容和功能清晰,易于交互。

使用大量留白。留白让重要内容和功能显得更加醒目。此外,留白可以传达一种平静和安宁的视觉感受,它可以使一个应用看起来更加聚焦和高效。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

让颜色简化UI一个主题色——比如在记事本中使用的黄色——让重要区域更加醒目并巧妙地表示交互性。这同时也给了一个应用一个统一的视觉主题。内置应用使用家族化的系统颜色,无论在深色和浅色背景上看起来都干净,纯粹。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

通过使用系统字体确保易读性。iOS的系统字体自动调整行间距和行的高度,使阅读时文本清晰易读,无论何种大小的字号都表现良好。无论你是使用系统或是自定义字体,务必使用动态型,这样你的应用可以在用户选择不同字号时做出应对。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

使用无边框的按钮。默认情况下,所有bar上的按钮都是无边框的。在内容区域,无边框按钮以文案、颜色以及操作指引标题来表明按钮功能。当按钮被激活时,该按钮呈现高亮的浅色状态来作为操作响应。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

1.1.3 用深度来体现层次(Use Depth to Communicate)

iOS经常在不同的层级上展现内容,用以表达分组和位置,并帮助用户了解在屏幕上的对象之间的关系。

通过使用一个在主屏幕上方的半透明背景浮层来区分文件夹和其余部分的内容。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

备忘录以不同的层级展示,如插图所示。用户在使用备忘录里的某个条目时,其他的条目被收起在屏幕下方(译者按:其实这个视觉提示使用起来很隐晦)。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

日历有较深的层级,当他们在翻阅年、月、日的时候,以及增强的交互动画给用户一种层级纵深感(循序切换的层次,从年到月到日)。在滚动年份视图时,用户可以即时看到今天的日期以及其他日历任务。当用户处于月份视图时,点击年份视图按钮,月份会缩小至年份视图中的所处位置。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

今天的日期依然处于高亮状态,年份出现在返回按钮处,这样用户可以清楚地知道他们在哪儿,他们从哪里进来并且知道如何返回。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

类似的过渡动画出现在用户选择一个日期时:月份视图从所选位置分开,将当前的周日期推向屏幕顶端并翻转出以小时为单位的当天时间视图。这些动画加强了日历上年月日之间的关系的感知度。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

1.2 iOS应用解析(iOS App Anatomy)

几乎所有的iOS 应用都应用了UIKit framework中定义的组件。了解这些组件的名字,作用和构成能够帮助你设计应用过程中做出更好的决定。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

UIKit提供的UI组件大致分成以下4种大类:

Bars包含了导航信息,告诉用户他们所在的位置并包含了一些能帮助用户浏览或启动某些操作的控制按钮。

内容视图:包含了应用的主体内容以及某些操作行为,比如滚动、插入、删除、排序等等。

控制按钮:展示信息或者控制动作。

临时视图:短时间出现,给用户重要信息或者额外的选择或者其他功能。

除了定义UI组件,UIKit也定义对象实现的功能,例如手势识别、绘图、辅助功能和打印支持。

从编程的角度来说,UI组件被认为是不同类别的视图,因为他们从UIView得到继承。视图能绘制屏幕内容并且知道用户何时触摸了屏幕。视图的所有类型有:控件(比如按钮和滑块),内容视图(比如集合视图和表格视图),以及临时视图(如警告提示和动作菜单)。

要在应用中管理一组或者一系列的视图,通常需要使用一个视图控制器,它能协调视图的显示内容,实现与用户交互的功能并能在不同屏幕内容之间切换。比如,“设置”使用了一个导航控制器来展示其视图层级。

下面是一个例子,关于视图与视图控制器如何结合并呈现iOS 应用的UI。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

虽然开发者认为真正起到作用的是视图和视图控制器,但一般用户感知到的iOS应用是不同屏幕内容的集合。从这个角度来看,在应用里,屏幕内容一般对应于一个独特的视觉状态或者模式。

注:一个iOS应用程序包含一个窗口。但是,不同于计算机程序中的窗口,iOS窗口没有可见的部分并且不能在屏幕上被移动到另一个位置。很多iOS应用程序只有一个窗口;可以支持外部显示设备器的应用程序可以有不止一个窗口。

iOS Human Interface Guidelines

中,

screen

这个词和大部分用户理解的一样。作为一个开发者,你也许需要读一下其他与UIscreen相关的章节,这样你可以更好的了解如何关联外部屏幕。

1.3 适应性和布局(Adaptivity and Layout)

1.3.1 为自适应而开发(Build In Adaptivity)

人们通常想随心所欲地使用自己喜欢的应用程序。在iOS 8及未来的版本中,你可以使用不同分辨率和自动布局来帮助你定义屏幕布局视图,视图控制器以及需要随显示环境改变的视图(显示环境display environment的概念指的是设备的整个屏幕或者其中一部分,比如一个跳出菜单区域或一个分视图控制器的主视图部分)。

iOS定义了两个尺寸类别(size class),常规的(regular)和压缩的(compact)。常规尺寸有着较易拓展的空间,而压缩尺寸约束了空间的使用。想要定义一种显示环境,你需要定义横纵尺寸类型。如你所想,iOS设备可以有横屏竖屏两种不同的使用模式。

iOS能随着显示环境和尺寸类别变化而自动生成不同布局。举个例子,当垂直尺寸从压缩变为常规时,导航栏和工具栏会自动变高。

当你靠尺寸类别来驱动布局变化时,你的应用在任何显示环境时都能显示得很好。关于如何在Interface Builder中更好的使用尺寸类别,你可以查阅

Size Classes Design Help

注:在一种尺寸类别中,持续使用Auto Layout进行小的布局调整,比如拉伸或压缩内容。

下面的实例可以帮助你理解尺寸类型是如何适配不同设备的显示环境。例如:iPad在长宽和横屏竖屏时都使用常规尺寸类型。换句话说,iPad显示环境一直处于垂直和水平的常规状态。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

iPhone的显示环境可根据不同的设备和不同的握持方向而改变。

竖屏时,iPhone6 Plus使用的是常规高度和压缩宽度类型。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

横屏时,iPhone6 Plus使用的是压缩高度和常规宽度类型。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

其他iPhone型号,包括iPhone6使用相同的尺寸类型设置。

竖屏时,iPhone 6,iPhone 5 和iPhone 4S使用的是压缩宽度和常规高度。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

横屏时,这些设备在宽高上使用的都是压缩类。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

1.3.2 在不同环境提供良好体验(Provide a Great Experience in Each Environment)

当你使用自适应来开发UI时,你可以保证UI跟随显示环境变化而适当地响应。遵照这些指南,你可以给用户良好的设备响应体验。

在所有环境下都保持对主体内容的专注。这是最高优先级。人们使用应用时,与感兴趣的内容发生互动。当使用环境变化的同时,改变专注点会让用户感觉到迷失方向,丧失了对应用的控制。

避免布局上不必要的变化。在所有环境中类似的使用体验让人们在旋转设备或在不同设备上运行你的应用时维持使用模式。例如,如果你使用一个网格在水平的常规模式下显示图像,你无需在一个列表中展示与压缩模式下相同信息,虽然你可能调整了网格的尺寸。

如果你的应用只在一个方向上运行,那么请直接一点。人们希望在各种握持方式下都可以使用你的应用,能满足这个期待是最好的。但是,如果你的应用只在一个方向下运行,那么以下几点请务必注意:

  • 避免提示人们旋转设备的提示UI出现。让应用清晰地运行在支持的方向下,让用户明白应该旋转设备,而不是添加不必要的引导性混乱。
  • 支持同一个方向上的变化。例如,如果一个应用只能垂直运行,用户无论用左手或是右手握持时都能触及到home键。如果用户在使用应用时180度旋转设备,那最好应用内容也能及时响应并旋转180度。

如果你的应用将设备方向翻转视为用户输入(的一种指令),那么就按照程序设定的方式来响应设备翻转。举个例子,一个游戏让用户利用设备翻转来移动游戏中的部件,那么这个游戏应用本身(的UI)不能对翻转屏幕产生响应。在这种情况下,你必须关联两个需要变化的方向,并且允许人们在这两个方向切换直到他们开始(了解并执行)应用的主体任务。一旦人们开始执行主要任务,那就开始按程序设定的那样来响应设备的动作吧。

1.3.3 使用布局来沟通(Use Layout to Communicate)

布局包含的不仅仅是一个应用屏幕上的UI元素外观。你的布局,应该告诉用户什么是最重要的,他们的选择是什么,以及事物是如何关联起来的。

提升重要内容或功能,让用户容易集中注意在主要任务上。有几个比较好的办法是在屏幕上半部分放置主要内容,以从左到右的习惯,从靠近左侧的屏幕开始。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

使用视觉化的重量和平衡向用户展示相关的屏显重要元素。大型控件吸引眼球,而比小的控件更容易在出现时被注意到。而且大型控件也更容易被用户点击,这让它们在应用中更加有用——就像电话和时钟(上面的按钮)——用户经常在容易分心的环境中能(正常)使用它们。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

使用对齐来让阅读更舒缓,让分组和层次之间更有秩序。对齐让应用看起来整洁而有序,也让用户在专注于屏幕时更有空间,从而专注于重要信息。不同信息组的缩进与对齐让它们之间的关联更清晰,也让用户更容易找到某个控件。

确保用户能明白处于默认尺寸的首要内容的含义。例如,用户无需水平滚动就能看到重要的文本,或不用放大就可以看到主体图像。

准备好改变字体大小。用户期望大多数应用能有设置字号大小的功能。为了适应一些文本大小的变化,你也许需要调整布局;想要得到更多文本显示相关的信息,你可以查阅章节Text Should Always Be Legible.

尽量避免UI上不一致的表现。在一般情况下,有着相似功能的控件看起来也应该类似。用户常常认为他们看到的不同总有原因,而且他们倾向于花时间去尝试(译者按:因此为了避免用户做无用的尝试所以建议类似功能外观一样)。

给每个互动的元素充足的空间,从而让用户容易操作这些内容和控件。常用的点按类控件的大小是44 x 44点(points)。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

1.4 起始与停止(Starting and Stopping)

1.4.1 即时启动(Start Instantly)

有种说法是用户往往不会花超过一两分钟去审视一个新应用,当你将应用从打开到启动这段时间压缩得很短,并且同时在载入过程中呈现一些对用户有帮助的内容,你就会激发用户的兴趣并给所有用户一个惊喜。

重要:不要在安装过程结束后告诉用户需要重启设备。重启需要时间并且会让人觉得你的应用看上去不可靠而且很难使用。

如果你的应用有内存使用问题,或者不重启就无法流畅运行,你必须声明这些问题。关于如何开发一款性能良好的应用,请查阅iOS应用编程指南

尽可能避免使用闪屏或者其他启动体验。用户能够在启动后立即开始使用应用是最好不过的。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

避免让用户做过多设置。而应该如此:

  • 聚焦在满足80%的用户需求上。这样主体用户群就无需设置各种选项,因为你的应用已经默认处于他们想要的状态。如果有些功能有少部分用户想要,换句话说,大部分人不需要的话,就别管它了。
  • 尽可能用其他方式获取更多(用户)信息。如果你能得到用户在内置应用或硬件设置中提供的信息,直接从系统中获取它们,而不需要再次让用户输入。
  • 如果你必须获取设置信息,在你的应用中直接向用户询问,然后尽快保存这些设定(译者注:这段讲的是权限许可,如能否访问照片或者日历或地理位置信息等等)。这样用户就无需强制跳出应用进入系统设置页面了。如果用户需要更改设置,他们可以在任何时候进入应用的设置选项进行修改。

尽可能让用户晚一些再登录。最理想的状态是,用户在无需登录的情况下就能尽量多地浏览内容并使用部分功能。例如,App Store应用会在用户浏览商品并确定进行购买时,才要求用户进行登录。对于必须登录才能进行后续浏览和操作的应用,用户往往会直接放弃。

如果你的应用必须先登录后使用,那么你应该在登录页面有一些简短的文字,来描述为什么必须先登录,以及这样做会给用户带来什么好处。

谨慎使用新手引导(介绍应用的功能和如何进行操作)。在考虑新手引导之前,你应该完善你的应用,尽可能使应用的功能直观和易于寻找。有句话说得好,

好的应用不需要新手引导

。如果你确实觉得需要新手引导,那么请参考如下的建议,设计一个简洁、有针对性并且不妨碍用户的新手引导。

  • 只提供开始使用应用所必需的信息。好的新手引导应该告诉用户接下来第一步应该做什么,或者简短地演示大部分用户感兴趣的一些功能。在能够浏览你的应用之前,如果用户遇到太多的信息,让用户记住这些不是当前所必须的内容,他们很可能会觉得你的应用很难用。如果在某些特定场景下确实需要一些引导,那么也应该在用户进入这个场景之后再进行。
  • 使用交互动画来吸引用户,并让用户通过实际上手来学习如何使用。对于文字内容的增加应该谨慎,且仅当增加文字对于提升体验有益时才这么做。不要指望用户会阅读大段的文字。例如,可以使用动画而不是文字来描述如何执行一个简单的任务。在引导用户了解较为复杂的任务时,可以通过一些引导浮层来简要说明每一个步骤用户需要做什么。尽可能避免展示应用截图,因为截图是死的,用户可能会混淆截图和应用的实际界面。
  • 能够简单地取消或者跳过新手引导。有些用户看完新手引导之后就不想再看,有些甚至根本就不想看新手引导。请记住用户的选择,不要强迫用户每次打开你的应用都要再做一次选择。

不要太早要求用户去给你的应用评分。过早要求用户进行评分可能会适得其反。如果你想获得用户有价值的反馈和评论,在邀请用户评论之前,请给他们一点时间来使用你的应用,并对你的应用形成印象。例如,你可以等用户访问了一定数量的页面或完成了一定数量的任务之后,再邀请他们进行评价。

一般建议按照屏幕默认的定向方式启动你的应用。尽管如此,如果你的应用只有一种屏幕方向,那么就始终以这个方向启动,让用户在他们自己需要时再改变设备方向。例如,一个游戏或者媒体观看应用只在横屏模式下运行,那么就应该以横屏模式启动,即使设备当前处于竖屏模式。这样的话,如果用户在竖屏模式下打开应用,他们也知道应该把设备转成横屏来进行使用。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

注:最好让横屏应用支持两种模式的横屏,即home键处于左右两侧的状态。如果设备当前已经处于横向状态,那么就按照当前状态启动应用,除非你有充分的理由不这么做。其他情况时,可以考虑按home键处于右侧的方式启动应用。(想要了解更多关于支持不同设备方向的内容,请参阅Respond to Changes in Device Orientation.)

准备一张与应用首页看上去一样的闪屏。iOS会在启动应用时调用这张图,这样可以让用户觉得启动速度很快,降低对等待时间的感知度。具体如何制作闪屏,请查阅Launch Images。(译者注:Launch Images章节处在iOS Human Interface Guidelines的Icon and Image Design部分,翻译将在后续更新中放出,烦请各位耐心等候。)

如果可能,不要让用户在初次启动应用时阅读免责声明或者确认用户协议。你可以直接在App Store展示这些内容,使用户在下载前就有所了解;虽然这个办法能最大地减少麻烦,但也不是一直可行。如果在某些情况下你必须展示这些内容,要确保它们与UI保持统一并在产品功能与用户体验之间达成平衡。

在应用重启后,需要恢复到用户退出使用时的状态,让他们可以从中断之处继续使用。无需让用户记住是如何达到此种退出状态的。

1.4.2 时刻准备好停止(Always Be Prepared to Stop)

iOS应用无需关闭或退出选项。当用户切换应用,回到主屏幕或者将设备调至睡眠模式的时候,其实就是停止了当前应用的使用。

当用户切换应用时,iOS的多任务系统会将其放置到后台并将新应用的UI替换上来。在这种情况下,你必须做到以下几点:

  • 随时并尽快保存用户信息,因为在后台的应用随时有可能被终止或退出。
  • 当应用停止的时候保存当前状态,使用户可以在回到应用时能从中断之处继续使用。例如,在使用可滚动的数据列表时,退出后保存列表所在的位置。

有些应用可能需要一直在后台运行。例如,用户可能希望能在使用一个应用的同时还能一直听歌,接着又想用另外一个应用来检查代办项或者玩游戏。关于如何正确处理多任务,请查阅Multitasking。(译者注:Multitasking章节处在iOS Human Interface Guidelines的iOS Technologies部分,翻译将在后续更新中放出,烦请各位耐心等候。)

不要强制让应用退出。因为这样会让用户误以为是crash。如果有问题产生,需要告诉用户具体状况以及如何解决。以下有两个建议,取决于出现的问题有多严重而酌情使用:

如果应用中所有的功能当前都不可用,那么应该显示一些内容来解释当前的情形,并建议用户如何进行后续操作。这部分内容给予了用户以反馈,使用户相信你的应用现在没问题。同时这也可以稳定用户情绪,让他们决定是否要采取纠正措施,继续使用应用,还是切换到另一个应用。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

如果只有部分功能不可用,那么只要当用户使用这些功能时显示提示即可。不然的话,用户就应该能正常使用应用的其他功能。如果你决定使用警告框来进行提示,请确保只在用户尝试使用不可用的功能时再显示。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

1.5 导航(Navigation)

除非导航设计的不合理,不然用户不应明显察觉到应用中的导航体验。放置导航到一个能够支撑你的应用结构和目的却又不过分引起用户注意的状态。

广义来说,有三种主要类型的导航,每种导航都有其适应的应用结构:

  • 分层。
  • 扁平。
  • 内容或经验驱动。

在分层应用中,用户在每个层级中都要选择其中一项,直到目的层级。如果要切换到另一个层级,用户必须回退一些层级,或者直接回到初始层级进行再次选择。系统的设置和邮件应用在这方面是很好的示范,可以参考他们。

w1

在扁平应用中,用户可以从一个主要分类直接切换到另一个,因为所有的主要分类都可以从主屏直接访问。音乐和App Store是两个使用扁平结构的好例子。

w2

在内容驱动或经验驱动信息结构的应用中,导航的内容也会根据内容或经验来进行设计。例如,在阅读一本电子书时,用户会一页接一页地进行阅读,也会在目录中选择想要阅读的页码跳转后开始阅读。同样的,在游戏应用中,导航的作用也非常重要。

w3

在某些情况下,在一个应用中结合多种导航类型会有很好的效果。例如,对于扁平信息结构中某一分类下的内容,用分层导航的方式来显示可能会更好。

用户应该时刻清楚自己当前在应用中所处的位置,以及如何前往他们所想到的页面。

无论导航类型是否适合你的应用结构,最重要的是用户访问内容的路径应该是合理、可预期和易于寻找的。

UIKit定义了一些标准的UI元素,这些元素即可以构建分层或扁平的导航,也可以实现以内容为中心的导航,例如电子书或者媒体观看类应用。游戏或者其他经验驱动的应用通常需要一些自定义的元素和行为。

使用导航栏(Navigation Bar)帮助用户轻松访问分层内容。导航栏的标题可以显示用户当前所处的层级,而后退按钮可以回到上一层级。查看Navigation Bar了解更多。

使用标签栏(Tab Bar)显示同类型的内容或功能。标签栏很适合于扁平信息结构,可以让用户在分类之间随意切换,而不用在意当前所处的位置。查看Tab Bar了解更多。

在应用中,如果每屏显示的都是同类项或同类页,可以使用页面控件(Page Control)。页面控件的优点是可以直观地告诉用户共有多少个项目或页面,以及当前所处的位置。查看Page Control了解更多。

一般来说,最好能给用户到达每一屏的路径。如果用户需要,就应该考虑使用临时视图,例如模态视图、动作菜单或警告框。查看Modal ViewAction SheetAlert了解更多。

(译者注:上文提到的章节均处在iOS Human Interface Guidelines的第4章,翻译将在后续更新中放出,烦请各位耐心等候。若有需要,亦可先参考先前已翻译的iOS7 UI Elements章节:。)

UIKit同时还提供了以下相关控件:

  • 分段控件(Segmented Control)。分段控件让用户在一屏内就可以查看到不同分类的内容,而不需要切换到其他屏幕。
  • 工具栏(Toolbar)。尽管工具栏看起来和导航栏或标签栏相似,但是工具栏不具导航作用。相反,工具栏为用户提供了可以控制当前屏幕内容的控件。

1.6 模态情境(Modal Contexts)

模态是一个承载某些连贯操作或内容的优缺点并存的模式。它可以给用户提供一种不脱离主任务的方式去完成一个任务或者获得信息,但是也会临时性地阻止用户对应用的其他部分进行交互操作。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

理想情况下,用户可以与iOS 应用进行一种非线性的交互,所以,尽可能减少应用中的模态体验是最好的。通常情况,在以下情形下可以考虑使用模态情境:

  • Ÿ必须引起用户关注的时候。
  • Ÿ 一个独立的任务需要完成或者很明确需要被放弃,为了避免在模棱两可的状态下遗漏用户信息的时候。

保持模态任务的简单,简短和高度聚焦。你肯定不希望用户使用模态视图时像使用应用中的一个mini应用一样。如果子任务过于复杂,用户会在进入模态情境时忽略主要任务。在设计一个涉及视觉层次的模态任务时特别要考虑这一点,因为用户有可能迷失并且忘记如何回到之前的操作中去。如果一个模态任务必须包含不同视图的子任务,确保给用户一个独立、清晰的导航路径,并避免迂回。

始终提供明显、安全的途径退出模态任务。确保用户在退出模态视图时可以预期操作的结果。

一个任务需要多层级的模态视图时,确保用户理解点击完成按钮的结果。点击一个低层级视图上的完成按钮是完成这个视图中任务的一部分,还是整个任务?因为存在这种困惑的可能性,所以要尽可能避免在下级视图中添加完成按钮。

保证提醒对话框的内容都是重要且可操作的。提醒对话框会打断用户的体验并且要点击才会消失,所以要让用户感到提醒信息是有用的,打断是有价值的。

尊重用户关于接收通知的选择。用户会设置接收应用通知的形式,必须尊重要用户的喜好设置,否则可能触怒用户,导致其关闭所有的推送通知。

1.7 交互性和反馈(Interactivity and Feedback)

1.7.1 用户知道标准手势(Users Know the Standard Gestures)

用户使用点击、拖拽、捏合等手势与iOS设备进行交互。使用手势拉近了用户和设备之间的距离,并且增强了直接操纵感。用户通常期待手势在不同应用之间都是通用的。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

除了用户熟悉的标准手势,iOS还定义了一些系统范围内的操作,例如呼出控制中心或消息中心。在任意应用下都可以使用这些操作。

不要给标准手势赋予不同的行为。除非你的应用是游戏,否则重新定义标准手势会使用户迷惑,且增加使用难度。

不要创建和标准手势功能相似的手势操作。用户已经习惯了标准手势的行为,没有必要让用户学习达到同样效果的不同操作。

可以用复杂手势作为完成某任务的快捷方式,但不能是唯一触达方式。最好给用户提供一些简单、直接的方式完成某操作,即使这种方法需要额外的动作。简单的手势能让用户集中于当前的体验和内容,而不是交互操作本身。

除非是游戏,否则避免定义新的手势。在游戏或其他沉浸式的应用中,操作手势也是有趣体验的一部分。但是在普通应用中,帮助用户达成目标要比操作本身重要的多,所以最好使用标准手势,尽量避免让用户去发掘和记忆新的操作。

在特定的环境中,可以考虑使用多指操作。虽然复杂的操作并不一定适用于所有应用,但对用户会花大量时间使用的应用来说可以丰富体验,例如游戏或创建内容环境。但因为非标准手势可发现性差,要尽量少用,并且不要让这类手势成为完成任务的唯一方式。

1.7.2 可交互元素吸引用户点击(Interactive Elements Invite Touch)

为了暗示交互性,设计时会使用很多线索,包括颜色、位置、上下文、表意明确的图标和标签等。并不需要过于修饰元素来向用户展示可交互性。

一个关键的颜色可以给用户提供很强的视觉指引,尤其是没有冗余的其它颜色时。为了有对比,使用蓝色标记可交互的元素,并且使用统一的、易识别的视觉风格。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

返回按钮使用多个线索指明其可交互性并传达其功能:它出现在导航中,显示了一个指向后方的图标,使用了关键色,显示了上一级页面的标题。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

一个图标或者标题提供了清晰的名称指引用户点击它。例如,地图中的标题“立交桥路线”,“定位到这里”,清晰地说明了用户可做的操作。结合关键色,就可以省去按钮边界或其他多余的修饰。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

在内容区域,有必要给按钮添加边界或背景。操作条中的按钮、动作表单和提醒对话框可以不需要边界,因为用户知道在这种区域中大多数选项是可交互的。但是在内容区域,按钮有必要使用边界或背景将按钮从其他内容中区分出来。例如,系统自带的音乐、时钟、照片和App Store应用会在一些特别的场景中使用这种按钮。

照片应用中给分享按钮增加了边框,与其他解释性文本进行了区分。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

时钟应用在秒表和计时页面中给按钮增加背景来强调开始和暂停按钮,并且使按钮在周围的内容中更容易点击。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

App Store应用中使用有边界的按钮,将按钮和整个内容条区分开来,点击整条内容查看详细信息,点击按钮进行下载安装。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

1.7.3 反馈有助于理解(Feedback Aids Understanding)

反馈会帮助用户了解应用当前在做什么,发现接下来可以做什么以及理解动作产生的结果。UIKit提供了很多反馈。

尽可能将状态或其他的反馈信息整合到UI中。用户不进行操作或不跳出当前内容就能获得需要的信息是最好的。例如,邮箱应用将当前邮箱的状态显示工具条上,这样就不会影响当前内容。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

避免显示不必要的警告框。警告框是一种很强的反馈机制,只有在传递非常重要也是理论上可行的信息时才需要使用它。如果用户常看到很多不是重要信息的警告框,他们很快就会忽略所有对话框提醒。

1.7.4 输入信息的方式要简单(Inputting Information Should Be Easy)

不管用户是点击控件还是使用键盘,输入信息都会花费时间和精力。如果发挥有用的效用前就让用户输入大量信息会减弱用户继续使用的欲望。

让用户更容易地进行选择。例如,使用选择器或者表格代替纯文本,避免要求用户打字来提高选择效率,降低选择成本。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

适宜地从iOS中获取信息。设备上存储了大量的用户信息。可以的话,不要让用户提供你可以轻易找到的信息,例如联系人或日历信息。

提供有用的反馈来平衡用户的输入。付出和回报的概念可以帮助用户感到进程在被推进。

1.8 动画(Animation)

iOS的用户界面中遍布着细微、精美的动画,它们使得应用的体验更具吸引力、更具动态性。适当的动画可以:

  • 传达状态和提供反馈
  • 增强直接操作的感觉
  • 帮助人们可视化他们的操作结果
q1

谨慎地增加动画,特别是在那些无法提供沉浸性体验的应用中。看起来过多的无理由的动画会阻碍应用的流畅性,降低性能,还会分散用户在任务中的注意力。尤其要说的是,要有目的和限制性地使用运动效果和UI组件中的动态行为,并确保对结果进行测试。一旦被合理的使用,这些效果能提高用户的理解度和愉悦度;过度使用他们则会使应用看起来很迷惑,很难控制。

在合适的时候,使自定义的动画与内置动画保持统一。人们习惯于谨慎添加动画,尤其是在那些不能提供沉浸式用户体验的应用中。如果应用主要关注一些严肃的任务或者生产性任务,那么动画就显得多余了,还会无端打乱应用的使用流程,降低应用的性能,让用户从当前的任务中分心。

开发者的自定义动画应该切合内置iOS应用的动画。用户习惯于内置iOS 应用使用的精细动画。事实上,用户趋向于把视图之间的平滑转换,对设备方向改变的流畅响应和基于物理力学的滚动效果看作是iOS体验的一部分。除非你的应用能够给用户沉浸式的体验–比如游戏–自定义动画应该可以与内置应用的动画相媲美。

使用风格类型一致的动画。在应用中使用风格类型一致的动画非常重要,可以让用户构建基于使用应用获得的用户体验。

大多数情况下,恰当一点的做法是让自定义动画更具现实性。用户乐意于接受自由的艺术创作,但当你的动画违背物理定律和自然法则的时候,他们会感觉到非常迷惑。

1.9 品牌推广(Branding)

品牌推广并不仅仅是在应用中展示品牌的颜色和logo。理想状态下,你开发的某个特定品牌的应用应该通过创建独特的外观和感觉来为用户提供难忘的体验。

在iOS系统之下可以很容易地使用自定义的图标、颜色和字体来创建区别于其他应用的UI。当你进行这些元素的设计时,牢记以下两点:

  • 每个自定义的元素本身都需要具备良好的观感和功能性,但它也应该与应用中其他元素保持一致,无论应用中其他元素是自定义的还是标准的。
  • 为了在iOS中感觉舒适,你的应用虽然不必看起来跟内置的一样,但是需要对它的遵从、清晰度和深度(如欲了解更多,参见1.1 为iOS而设计(Design for iOS))进行整合。花些时间弄清楚在你的应用中遵从、清晰和深度所代表的意味,并把它们在你的自定义元素中表达出来。

当你需要让用户意识到你的品牌时,你应该遵循以下几点:

以精致优雅不唐突的方式植入品牌的颜色和图片。用户使用你的应用来完成事务或者进行娱乐,他们不希望被强迫着去观看广告。为了获得最好的用户体验,你可以通过字体、颜色和图像的设计来潜移默化地地提醒用户你的品牌身份。

避免远离用户关心的内容。比如,在屏幕顶部展示一个二级栏目,仅用来展示品牌资产,这意味着内容没有足够的空间,可以考虑以其他低侵入性的方法无处不在地展示品牌,比如巧妙地定制屏幕的背景。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

抵抗住诱惑,不要把你的logo贯穿整个应用。移动设备的屏幕多数相当小,logo的每一次出现都会占据空间而将用户与他们想看的内容隔离开。而且,在应用中显示logo并不能像在网页中显示logo那样达到相同的目的:对于用户来说通常会很容易在不知道网页所属的情况下访问一个网页,但却极少有用户会在完全不看一个iOS系统中的应用图标的情况下就打开它。

1.10 颜色与字体(Color and Typography)

1.10.1 色彩有助于增进沟通(Color Enhances Communication)

在iOS系统中,颜色会用于表征交互,传递活性以及提供视觉连续性。内置的应用程序选择使用那些看起来更具个性的、纯粹、干净的颜色,并辅以或亮或暗的背景组合。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

如果你要创建多样的自定义颜色,要确保它们能够和谐共存。例如,如果你的应用的基本风格是柔和的色调,你就应该创建一个协调的柔和色调的色板用于整个应用。

注意在不同情境下的颜色对比。例如,如果在导航栏的背景与栏按钮标题之间没有足够的对比,按钮就会很难被用户看到。 依据经验的法则来说,需要区分的颜色必须至少存在50%的亮度差异。(我们)需要将设备置于不同的光照环境之中(包括晴朗的室外)来测试设备上的观感效果。

提示:一种发现需要更高对比度的区域的方法是降低UI的饱和度并在灰度模式下查看它。如果在灰度版本中你很难区分可交互与非可交互元素或背景等,你有可能需要增加这些元素之间的对比度。

当你使用自定义的栏颜色时,着重考虑半透明的栏和应用内容。当你需要创建能匹配特别颜色的栏颜色时(比如一个已有品牌中的颜色),可能在你获得你想要的结果之前,你需要用各种颜色进行实验。栏的显示将会同时受到iOS系统所提供的半透明栏与藏在栏后面的应用内容的呈现所影响。

API注释:使用浅色(TintColor)的属性值给予栏按钮颜色,使用栏浅色(BarTintColor)的属性值为栏本身赋色。欲了解更多关于栏属性的内容,可参见

UINavigationBar Class Reference

,,UITabBar Class Reference

UIToolbar Class Reference

UISearchBar Class Reference

(译者注:相关章节翻译将在后续更新中放出,烦请各位耐心等候。)

注意颜色的盲区。多数色盲的人很难区分红色与绿色。需要对你的应用进行测试以确保在其中你没有将红色与绿色作为区分两个不同状态或值的唯一方式,一些图像编辑软件或工具能够有效的帮你验证颜色的盲区。通常意义来说,使用多种方式来表征原色的交互性是非常好的(需要了解更多关于在iOS系统中表征交互性的信息,详见Interactive Elements Invite Touch)。

考虑选择一种基准色颜色来表征交互性与状态。在内置的应用中基准色有比如备忘录中的黄色与日历中的红色等。如果你定义一种用于表征交互和状态的基准色,要确保你的应用中的其他颜色不会与它发生冲突。

色彩可以向用户传达信息,但不一定会以你希望的方式。每个人眼中的色彩是不一样的,不同的文化为色彩赋予的意义也是不相同的。花时间来研究如何使用色彩才可能会被其他国家或者文化接受。你要尽可能确定应用中运用的色彩向用户传达了恰当的信息。

大多数情况下,不能让颜色喧宾夺主,让用户分心。除非色彩是应用的目的和本质所在,通常情况下色彩应该用来从细微细节之处提升用户体验。

1.10.2 文字应该清晰易读(Text Should Always Be Legible)

文字首先必须是清晰可辨的。如果用户不能看清楚应用中的字词,那么文字再好看也是没是无意义的。当你在你的应用中采用Dynamic Type时,你可以实现:

  • 能自动调整文字的粗细,字母间距以及行高。
  • 为语义上有区别的文本模块指定不同的文本样式,比如正文、脚注或者标题。
  • 文本可以根据用户在动态文字和可访问性设置中指定字体大小的变化作出适当的响应。

注:如果你是用自定义字体,你仍然可以依据系统的字号设置来规划字体范围。当用户改变设置时,你的应用也必须响应式的配合。

就你而言,要采用Dynamic Type需要一些工作。为了学习如何使用文字样式并确保当用户改变文字型号设置时你的应用能够获取通知,可以参考

Text Styles

in

Text Programming Guide for iOS

(译者注:相关章节翻译将在后续更新中放出,烦请各位耐心等候。)

文本尺寸的响应式变化需要优先考虑内容。并不是所有的内容对于用户都是同等重要的。当用户选择更大的文本尺寸时,他们是想要使他们关注的内容更容易阅读;他们并不总是想要屏幕上的每个单词都更大。

例如,当用户选择具备更大易用性的文本尺寸时,邮件将会以更大的尺寸显示邮件的主题和内容,而对于那些没那么重要的信息——如时间和收件人——则采用较小的尺寸。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

在适当的情况下,当用户选择一个不同的文本尺寸时要调整页面布局。例如,当用户选择小的文本尺寸时,你可能想将内容由一列的布局方式改为两列。如果你决定根据不同的文本尺寸调整布局,你可以选择针对尺寸的子集来实现——如包含小,中和大尺寸——而不是对于每个可能的尺寸都进行布局的调整。

确保一个自定义字体在不同尺寸下的所有类型都具备可读性。实现这一效果的方法之一是效仿在不同的文本尺寸下iOS系统呈现字体样式的一些方法。例如:

  • 文本永远都不应该小于11点(points),即使是用户选择极小的文本尺寸。相较而言,内容样式使用17点的字号作为大尺寸的默认文本尺寸设置。
  • 通常来说,字号与行距值在每一档的文本尺寸设置中差别为1点。唯一例外的是两种标题的样式,它们被应用在极小、小和中尺寸的设置中,使用了相同的字号、行距和字距。
  • 在最小的三种文本尺寸中,字间距相对较大;而在最大的三中文本尺寸中,字间距相对紧凑。
  • 标题和内容的样式使用相同的字体尺寸,同时,为了区分标题与内容样式,标题样式使用更重的值。
  • 导航控制栏的文本使用相同的字号,而内容文本的样式则使用大尺寸的设置(值为17点)。
  • 文本总是使用常规或者中重,一般不适用轻或者加粗。

通常情况下,应用中整体应该使用单一字体。多种字体的混杂会使你的应用看上去支离破碎和草率。相反,使用一种字体和少数样式。根据语义用途,使用UIFont类的API来定义不同文本区域的样式,比如正文或者标题。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

1.11 图标和图形(Icons and Graphics)

1.11.1 应用图标(The App Icon)

每个应用都需要一个漂亮的图标。用户常常会在看到应用图标的时候便建立起对应用的第一印象,并以此评判应用的品质、作用以及可靠性。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

以下几点是你在设计应用图标时应当记住的。当你确定要开始设计时,请参考App Icon来获取更详细的设计规格与指导。(译者注:App Icon章节处在iOS Human Interface Guidelines的Icon and Image Design部分,翻译将在后续更新中放出,烦请各位耐心等候。)

  • 应用图标是整个应用品牌的重要组成部分。将图标设计当成一个讲述应用背后的故事,以及与用户建立情感连接的机会。
  • 最好的应用图标是独特的,整洁的,打动人心的,让人印象深刻的。
  • 一个好的应用图标应该在不同的背景以及不同的规格下都同样美观。为了丰富大尺寸图标的质感而添加的细节有可能让图标在小尺寸时变得不清晰。

1.11.2 栏图标(Bar Icons)

iOS提供了一系列小的icon,用以代表各种常见任务与操作,它们常用在标签栏(Tab Bar)、工具栏(Toolbars)与导航栏(Navigation Bar)中。用户通常都已经了解这些内置图标的含义了,因此可以尽可能的多使用它们。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

如果需要自定义动作或者内容,你也可以设计自定义图标。设计这些小的线性图标与设计应用图标有很大的区别,请参考Bar Button Icons来了解更多内容。(译者注:Bar Button Icons章节处在iOS Human Interface Guidelines的Icon and Image Design部分,翻译将在后续更新中放出,烦请各位耐心等候)

请注意,你有时候也可以用文字来代替工具栏和导航栏的图标。 就像iOS的日历里面,工具栏上便是使用“今天”、“日历”和“收件箱”来代替图标进行表意的。

[ISUX转译]iOS 8人机界面指南(一):UI设计基础

想要决定在工具栏和导航栏中到底是用图标还是文字,可以优先考虑一屏中最多会同时出现多少个图标。如果数量过多,可能会让整个应用看起来难以理解。使用图标还是文字还取决于屏幕方向是横向还是纵向,因为水平视图下通常会拥有更多的空间,可以承载更多的文字。

1.11.3 图形(Graphics)

iOS应用大多数图形丰富。无论是你需要展示用户的照片,还是需要创建自定义图片,以下这些需求都应该遵守:


  • 支持Retina显示

iOS应用安全开发,你不知道的那些事

本文作者唐巧_boy

联网领域,安全已然是一个老生常谈的话题。许多大公司都设置有专门的安全部门,用于检测自己产品的安全性。但即便是这样,业界仍然时常爆出许多安全问题引发的新闻。就在不久前,乌云曝光了携程网在支付过程中,为了调试方便,记录了用户的信用卡卡号和CVV码等信息,而调试接口可以被外网访问,这样造成黑客可能通过调试接口读取用户的信用卡信息。虽然最终没有造成实际上的用户损失,但此次事件再一次给互联网公司敲响了安全的警钟。

除了国内,国外的互联网安全问题同样让人担忧。2013年由于曝出Apache Struts2的漏洞,苹果公司多次重置开发者的密码,并且最终为了用户数据的安全,将整个开发者后台全部停止服务,花了2周多时间将后台有潜在问题的功能重写后,才重新开放服务。而4月份刚刚曝光的OpenSSL的漏洞,则让全球三分之二的网站受到影响。在移动互联网快速发展的今天,iOS应用由于直接运行在用户的手机上,相比运行在服务器的后台服务,更有可能被黑客攻击。本文接下来将从三个方面概述iOS移动应用在安全方面所面临的挑战以及应对措施。

网络安全

1. 安全地传输用户密码

大部分的iOS应用都需要连网,通过和服务器端进行通信,获得最新的信息并且将内容展现给用户。由于网络传输过程中有可能经过不安全的中间节点,所以我们应该对敏感数据加密,用于保证用户信息的安全。黑客可以在受害者的手机上设置网络通信的代理服务器,从而截获所有的网络请求。即使是HTTPS的加密通信,黑客也可以通过中间人攻击(Man-In-The-Middle Attack,指的是攻击者与通信的两端分别创建独立的联系,并交换其所收到的数据,使通信的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上,整个会话都被攻击者完全控制)来截取通信内容。

黑客可以在Mac下使用Charles软件(如果在Windows下,可以使用Fiddler软件)来将自己的电脑设置成代理服务器,从而截取应用的网络请求,分析目标应用在通信协议上是否有安全问题。为了测试,我选取了在国内最大的两家租车公司(神州租车和一嗨租车)的iOS应用。

从图1可以看到,神州租车和一嗨租车在用户登录时,均采用明文的方式,将密码直接发送给服务器。其中一嗨租车不但采用明文方式发送密码,而且在发送时使用了Http Get的方式,而GET的URL数据一般都会保存在服务器的Access Log中,所以黑客一旦攻破服务器,只需要扫描Acesss Log,则可以轻易获得所有用户的明文密码(在本文发表前,一嗨租车已修改了登录协议,采用了POST的方式来登录,但仍然传递的是明文密码)。





图1 神州租车和一嗨租车的登录协议

如果每一个移动应用都像以上两种应用那样,明文传输用户密码,那么我们可以想象这样一个场景:黑客在咖啡馆或机场等一些公共场所,将自己的电脑设置成与该场所一样名字的免费Wi-Fi,受害者只要不小心使用了该Wi-Fi,则可能泄漏自己的明文密码。对于大多数普通人来说,他们会使用一样的密码登录他的所有的账号,这就意味着他的其他账号:例如淘宝或网上银行账号也有被盗的风险。

正确的做法应该是这样:事先生成一对用于加密的公私钥,客户端在登录时,使用公钥将用户的密码加密后,将密文传输到服务器。服务器使用私钥将密码解密,然后加盐(Salt,在密码学中是指,通过在密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这个过程称之为“加盐”),之后再多次求MD5,然后再和服务器原来存储的用同样方法处理过的密码匹配,如果一致,则登录成功。这样的做法,保证黑客即使截获了加密后的密文,由于没有私钥,也无法还原出原始的密码。而服务器即使被黑客攻陷,黑客除了暴力尝试,也无法从加盐和多次MD5后的密码中还原出原始的密码。这样就保证了用户密码的安全。

2. 防止通信协议被轻易破解

除了上面提到的明文传输密码的问题外,移动端应用还要面对黑客对于通信协议的破解的威胁。

在成功破解了通信协议后,黑客可以模拟客户端登录,进而伪造一些用户行为,可能对用户数据造成危害。例如腾讯出品的消除游戏《天天爱消除》,在淘宝上就有很多售价仅为1元的代练服务,如果真正是人工代练,是不可能卖这么便宜的,只有可能是该游戏的通信协议被破解,黑客制作出了代练的机器人程序。通信协议被破解除了对于移动端游戏有严重危害外,对于应用也有很大的危害。例如针对微信,黑客可以制作一些僵尸账号,通过向微信公共账号后台发送垃圾广告,达到赢利的目的。而iPhone设备上的iMessage通信协议居然也被破解了,所以很多iPhone用户会收到来自iMessage的垃圾广告。

对于以上提到的问题,开发者可以选择类似ProtoBuf(Google提供的一个开源数据交换格式,其最大的特点是基于二进制,因此比传统的JSON格式要短小得多)之类的二进制通信协议或自己实现通信协议,对于传输的内容进行一定程度的加密,以增加黑客破解协议的难度。图2是我截取的淘宝客户端的通信数据,可以看到其中的值都不能直观地猜出内容,所以这对于通信协议是有一定的保护作用。



图2 淘宝客户端通信协议

3. 验证应用内支付的凭证

iOS应用内支付(IAP)是众多应用赢利的方式,通过先让用户免费试用或试玩,然后提供应用内支付来为愿意付费的用户提供更强大的功能,这种模式特别适合不习惯一开始就掏钱的中国用户。但由于国内越狱用户的比例比较大,所以我们也需要注意应用内支付环节中的安全问题。简单来说,越狱后的手机由于没有沙盒作为保护,黑客可以对系统进行任意地修改,所以在支付过程中,苹果返回的已付款成功的凭证可能是伪造的。客户端拿到付款凭证之后,还需要将凭证上传到自己的服务器上,进行二次验证,以保证凭证的真实性。

另外,我们发现越狱用户的手机上,很可能被黑客用中间人攻击技术来劫持支付凭证。这对于黑客有什么好处呢?因为苹果为了保护用户的隐私,支付凭证中并不包含任何用户的账号信息,所以我们的应用和服务器无法知道这个凭证是谁买的,而只能知道这个凭证是真的还是假的。所以在验证凭证时,哪个账号发起了验证请求,我们就默认这个凭证是该账号拥有的。如果黑客将凭证截获,就可以伪装成真实用户来验证凭证或者转手出售获利。

打个比方,这就类似于很多商场的购物卡一样,由于是不记名的,黑客如果将你买的购物卡偷窃然后去刷卡购物,商场是无法简单地区分出来的。因此,对于应用内支付,开发者除了需要仔细地验证购买凭证外,也需要告知用户在越狱手机上进行支付的风险。

本地文件和数据安全

1. 程序文件的安全

iOS应用的大部分逻辑都是在编译后的二进制文件中,但由于近年来混合式(Hybrid)编程方式的兴起,很多应用的部分功能也采用内嵌Web浏览器的方式来实现。例如腾讯QQ iOS客户端的内部,就有部分逻辑是用Web方式实现的。由于iOS安装文件其实就是一个zip包,所以我们可以通过解压,看到包内的内容。以下是我解开腾讯QQ客户端,看到的其qqapi.js文件的内容。



可以看到,这些文件都有着完整清晰的注释。通过分析这些JavaScript文件,黑客可以很轻松地知道其调用逻辑。在越狱手机上,还可以修改这些JavaScript代码,达到攻击的目的。

我也曾尝试查看支付宝客户端中的彩票功能,通过分析,也可以找到其完整的、带着清晰注释的JavaScript代码,如图3所示(支付宝现在已对相应代码进行了加密)。

通过将JavaScript源码进行混淆和加密,可以防止黑客轻易地阅读和篡改相关的逻辑,也可以防止自己的Web端与Native端的通信协议泄漏。



图3 支付宝应用的JavaScript文件

本地数据安全

iOS应用的数据在本地通常保存在本地文件或本地数据库中。如果对本地的数据不进行加密处理,很可能被黑客篡改。比如一款名为《LepsWorld 3》的游戏,打开它的本地文件,可以很容易地找到,它使用了一个名为ItempLifes的变量保存生命数值(如图4所示)。于是我们可以简单修改该值,达到修改游戏参数的目的。而在淘宝上,也可以找到许多以此挣钱的商家。对于本地的重要数据,我们应该加密存储或将其保存到keychain中,以保证其不被篡改。



图4 《LepsWorld 3》的本地数据

源代码安全

通过file、class-dump、theos、otool等工具,黑客可以分析编译之后的二进制程序文件,不过相对于这些工具来说,IDA的威胁最大。IDA是一个收费的反汇编工具,对于Objective-C代码,它常常可以反汇编到可以方便阅读的程度,这对于程序的安全性,也是一个很大的危害。因为通过阅读源码,黑客可以更加方便地分析出应用的通信协议和数据加密方式。

下面分别示例了一段代码的原始内容和通过IDA反汇编之后的结果。可以看到,IDA几乎还原了原本的逻辑,而且可读性也非常高。

原始代码:



反汇编后:



反汇编的代码被获得后,由于软件内部逻辑相比汇编代码来说可读性高了很多。黑客可以用来制作软件的注册机,也可以更加方便地破解网络通信协议,从而制作出机器人(“僵尸”)账号。最极端的情况下,黑客可以将反汇编的代码稍加修改,植入木马,然后重新打包发布在一些越狱渠道上,这将对用户产生巨大的危害。

对于IDA这类工具,我们的应对措施就比较少了。除了可以用一些宏来简单混淆类名外,也可以将关键的逻辑用纯C语言来实现。例如微信的iOS端的通信底层,就是用C语言实现的。这样的方式除了能保证通信协议的安全外,也可以在iOS和Android等多个平台使用同一套底层通信代码,达到复用的目的。

总结

由于移动互联网的快速发展,人们的购物、理财等需求也在移动端出现,这使得移动应用的安全性越来越重要。由于部署在用户终端上,移动应用比服务器应用更容易被攻击,大家也需要在移动应用的网络通信、本地文件和数据、源代码三方面做好防范,只有这样才能保证应用的安全。

iOS APP 架构漫谈二

本文转自*不会开机的男孩*:
http://studentdeng.github.io/blog/

上一篇《iOS APP 架构漫谈》简单介绍了information flow的概念。这篇文章简单介绍另一个在编程中非常重要的思想或工具——状态机(State machine)。对大多数计算机专业的家伙们来说,这应该是一门比较难学的课程,里面包含一大堆揪心的名字比如DFA,NFA,还有一大堆各种各样的数学符号,又是编译原理的基础。不过很遗憾,似乎在做完编译原理课程作业之后,很多人再也没有实现过或是用过状态机了。本文通过一个游戏demo来简单描述一下状态机在实践中的应用。demo code

背景

首先看下我们的使用场景,假如我们需要设计一套联网对战的小游戏。第一个难题可能是如何建立一个通道,让2个手机相互发送消息。这里我并不打算引入server端开发,希望只是通过客户端来实现这个逻辑,这里使用LeanCloud API来简化这个过程。这样我们可以暂时不考虑技术细节,直接站在业务角度去思考如何建立这个游戏。

业务场景–邀请

正式开始游戏之前,总会有一个邀请的环节。假如我们有2个用户,分别是Host,Guest。Host创建游戏,Guest加入游戏。游戏的整个流程和我们平时玩的对战游戏流程并没有多大不同。

image

  1. Host创建游戏,他就相当于进入一个等待队列里面。
  2. Guest加入游戏,他从等待队列中找到一个匹配,比如Host。然后对Host发送join message
  3. Host会收到很多join message。由于我们只是选择1vs1。这里假定Host同意Guest加入游戏。Host向Guest发送join confirm message
  4. Guest收到join confirm message, 向Host发送Go消息,表示Guest已经进入游戏
  5. Host收到Go消息。也进入游戏。

具体实现业务逻辑

现在的构想的逻辑只有5步,但其实还会包含很多逻辑,比如超时机制,重发机制。由于中间状态很多,还可能有我们没有想到过的问题。在面对这种复杂逻辑时,会通过状态机来帮助我们理顺逻辑。这时,我们脑中思考的业务其实是一个状态到一个状态的图。 如下

image

上半部分是游戏的创建者,下半部分是游戏的加入者。

一开始,尽量简化模型,这里红色剪头表示我们的正确主流路线,黑色表现出错路线。也就是说,一旦错误,就回到原始Idle状态。

开始写代码

在想清楚所有逻辑,并考虑清楚正常路线和错误路线之后,就可以开始写代码了。为了方便,这里直接使用第三方的状态机框架TransitionKit

定义State(HOST)

 TKState *idleState = [TKState stateWithName:@"idle"];
  TKState *waitingJoinState = [TKState stateWithName:@"waitingJoin"];
  TKState *waitingConfirmState = [TKState stateWithName:@"waitingConfirm"];
  TKState *goState = [TKState stateWithName:@"go"];

  [waitingConfirmState setDidEnterStateBlock:^(TKState *state, TKTransition *transition) {
      [selfWeak sendJoinConfirm];
  }];

  [goState setDidEnterStateBlock:^(TKState *state, TKTransition *transition) {
      NSLog(@"happy ending");

      [SVProgressHUD showSuccessWithStatus:@"ok"];
  }];

定义Event(HOST)

Event 是建立State到State的路径

TKEvent *waitingJoinEvent = [TKEvent eventWithName:CUHostGameManagerWaitingJoinEvent
                           transitioningFromStates:@[idleState]
                                           toState:waitingJoinState];

  TKEvent *receiveInviteEvent = [TKEvent eventWithName:CUHostGameManagerReceiveInviteEvent
                               transitioningFromStates:@[waitingJoinState]
                                               toState:waitingConfirmState];

  TKEvent *receiveConfirmEvent = [TKEvent eventWithName:CUHostGameManagerReceiveConfirmEvent
                                transitioningFromStates:@[waitingConfirmState]
                                                toState:goState];

  TKEvent *disconnectedEvent = [TKEvent eventWithName:CUHostGameManagerDisconnectedEvent
                               transitioningFromStates:nil
                                              toState:idleState];

定义过程(HOST)

- (void)startGame {

      NSAssert(self.session.peerId != nil, @"");

      //这里,如果不是idle,我们切换状态机到idle
      if (![self.stateMachine.currentState.name isEqual:@"idle"]) {
          [self fireEvent:CUHostGameManagerDisconnectedEvent userInfo:nil];
      }

      //这里调用LeanCloud 入队
      AVObject *waitingId = [AVObject objectWithClassName:@"waiting_join_Ids"];
      [waitingId setObject:self.session.peerId forKey:@"peerId"];
      [waitingId saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
          //enqueue 之后,进入waitingJoin状态
          [self fireEvent:CUHostGameManagerWaitingJoinEvent userInfo:nil];
      }];
  }

  - (void)sendJoinConfirm {
      //发送加入确认消息给Guest
      AVMessage *message = [AVMessage messageForPeerWithSession:self.session
                                                   toPeerId:self.peerId
                                                    payload:@"join_confirm"];
      [self.session sendMessage:message transient:YES];
  }

  - (void)session:(AVSession *)session didReceiveMessage:(AVMessage *)message
  {
      if ([message.payload isEqualToString:@"join"]) {
          //收到Join(邀请)之后,发送确认消息
          self.peerId = message.fromPeerId;

          //因为LeanCloud的API比较挫,watch 之后才能发送消息,但是我们不知道什么时候才watch成功。。。。
          //好在只是demo,我们只好用这种方式work around,延迟2s发送消息
          [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(sendInviteConfirmRequest:) object:nil];
          [self performSelector:@selector(sendInviteConfirmRequest:)
                      withObject:@[message.fromPeerId]
                      afterDelay:2.0f];
      }
      else if ([message.payload isEqualToString:@"go"]) {
          //收到go消息,流程结束
          [self fireEvent:CUHostGameManagerReceiveConfirmEvent userInfo:nil];
      }
  }

  - (void)sendInviteConfirmRequest:(NSArray *)watchPeerIds {
      [self.session watchPeerIds:watchPeerIds];
      [self fireEvent:CUHostGameManagerReceiveInviteEvent userInfo:nil];
  }

定义State(Guest)

TKState *idleState = [TKState stateWithName:@"idle"];
  TKState *waitingReplyState = [TKState stateWithName:@"waitingReply"];
  TKState *goState = [TKState stateWithName:@"go"];

  [waitingReplyState setWillEnterStateBlock:^(TKState *state, TKTransition *transition) {
      [selfWeak searchingGames];
  }];

  [goState setDidEnterStateBlock:^(TKState *state, TKTransition *transition) {
      [selfWeak sendGo];
      NSLog(@"happy ending");
      [SVProgressHUD showSuccessWithStatus:@"ok"];
  }];

定义Event(Guest)

TKEvent *searchingEvent = [TKEvent eventWithName:CUGestGameManagerSearchingEvent
                           transitioningFromStates:@[idleState]
                                           toState:waitingReplyState];

  TKEvent *receiveConfirmEvent = [TKEvent eventWithName:CUGestGameManagerReceiveConfirmEvent
                                transitioningFromStates:@[waitingReplyState]
                                                toState:goState];

  TKEvent *disconnectedEvent = [TKEvent eventWithName:CUGestGameManagerDisconnectedEvent
                              transitioningFromStates:nil
                                              toState:idleState];

定义过程(Guest)

- (void)joinGame {

  if (![self.stateMachine.currentState.name isEqual:@"idle"]) {
    [self fireEvent:CUGestGameManagerDisconnectedEvent userInfo:nil];
  }

  [self fireEvent:CUGestGameManagerSearchingEvent userInfo:nil];
}

- (void)searchingGames {
  AVQuery *query = [AVQuery queryWithClassName:@"waiting_join_Ids"];
  [query orderByDescending:@"updatedAt"];
  [query setLimit:1];

  [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
    NSMutableArray *installationIds = [[NSMutableArray alloc] init];
    for (AVObject *object in objects) {
      if ([object objectForKey:@"peerId"]) {
        [installationIds addObject:[object objectForKey:@"peerId"]];
      }
    }

    [self.session watchPeerIds:installationIds];

    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(sendJoinRequest) object:nil];
    [self performSelector:@selector(sendJoinRequest)
               withObject:nil
               afterDelay:2.0f];
  }];
}

- (void)sendJoinRequest {

  for (NSString *item in self.session.watchedPeerIds) {
    AVMessage *message = [AVMessage messageForPeerWithSession:self.session
                                                     toPeerId:item
                                                      payload:@"join"];
    [self.session sendMessage:message transient:YES];
  }
}

- (void)sendGo{
  AVMessage *message = [AVMessage messageForPeerWithSession:self.session
                                                   toPeerId:self.otherPeerId
                                                    payload:@"go"];
  [self.session sendMessage:message transient:YES];
}

最后

state machine 是一个蛮厉害的锤子,只要是一个工具,就肯定会被滥用。。。state machine最大的好处是在于,方便我们思考清楚所有细节,主线,和错误流程。避免因为考虑不周全而产生的bug。结合之前的information flow的思路,会让我们的软件设计更加清楚。

demo code

?>