iOS编程实战 — 新的UI范式

iOS 7给苹果设备带来了全新的用户界面(UI)。iOS 7在UI上的变化是自其诞生以来最大的。iOS 7专注于三个重要的特点:清晰、依从和层次。理解这三个特点很重要,因为这有助于设计跟原生的系统内置应用一样的应用。

本章将介绍iOS 7引入的一些重要变化以及如何让应用使用这些新特性。前半章展示开发者应该了解的重要的UI范式,利用这些范式可以使应用更上一层楼。后半章展示如何把已有的iOS 6应用迁移到iOS 7上,并且需要的话,也可以保持向后兼容性。

2.1 清晰、依从和层次

清晰(clarity)很简单,就是对用户来说表达的意思是清晰的。大部分iOS用户会抓住一些碎片时间使用应用,他们会打开Facebook,查看通知或者新闻订阅,发布状态更新或照片,然后关闭应用。至少在iPhone上,很少有应用是用户长时间使用的。事实上统计表明,对于大部分应用,用户每次只会用80秒左右。这意味着开发者必须在极短的时间内把信息传递给用户,因此应该把最重要的信息清晰地展示在屏幕上。举个例子,如果开发天气应用,温度和天气状况是用户最感兴趣的两块信息,要把UI设计得足够简洁,让用户不需要在视野范围内寻找就能直接看到。

依从(deference)是说操作系统不会提供和应用的UI竞争的UI,相反,它依从于应用及其内容。这意味着每个应用都会有独一无二的外观和体验。在iOS 7之前,应用的UI是用户看到的在系统提供的导航栏和工具栏内部的东西,镀层效果、不规则边框和渐变色被依从于其背后内容的半透明栏取代。操作系统的默认应用依从于内容,而UI在大部分情况下只起辅助作用。

为iOS 7设计的应用应该具有层次(depth)的概念。控件和内容应该处于不同的层,从home screen开始就是这样,视差效果给人感觉图标是浮在最上面的一层上,模态化的警告框也有类似效果。通知中心和控制中心也有层次,它们用的是模糊和半透明而不是视差效果。

2.2 动画、动画、动画

使用iOS 7后开发者发现的第一件事可能就是应用的视觉拟真(拟物)没有了。阴影很不明显,几乎看不出来,抛光按钮完全消失了。真实世界的拟物元素,比如备忘录(iPhone)和通讯录(iPad)的人造革,也都不见了。

最重要的是,应用程序窗口默认就是全屏的,状态栏现在是半透明的,浮在应用程序窗口的上方。注意,直到iOS 6,应用程序的全屏和状态栏的半透明一直都只是可选的。在iOS 6中,必须把导航栏的translucent属性设置为YES,并且把视图控制器的wantsFullScreenLayout设置为YES才行。在iOS 7中所有的窗口默认都是这么设置的。

一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:1012951431, 分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!希望帮助开发者少走弯路。

尽管视觉拟真没有了,iOS 7强化了运动拟真。运动拟真的强化从锁屏开始。试着拖动摄像头按钮把锁屏界面“提起来”,然后从屏幕中间“掉下来”,你会看到锁屏界面会在碰到屏幕底部时弹起来。现在再试着把锁屏界面往屏幕底部推一下来“撞击”,你会看到弹得更厉害了,就跟真实世界的物体撞到表面一样。所以,拟物并没有完全从iOS 7中消失,之前版本的iOS强调视觉拟物,而iOS 7更加重视物理拟物。


iOS 7仍然是拟物的,只不过不是强调视觉拟物而是物理拟物,屏幕上的物体遵从物理定律,其行为类似于真实世界的物体。


2.2.1 UIKit Dynamics

在应用中实现物理特性很简单,iOS 7引入了一个新类UIDynamicAnimator,用来模拟真实世界的物理定律。开发者可以创建一个UIDynamicAnimator对象并将其添加到视图上,这个视图通常是视图控制器的根视图,在UIKit Dynamics上下文中通常也被称为引用视图,引用视图的子视图现在就会基于其关联的行为遵从物理定律。

行为可以通过声明定义并添加到视图。事实上,任何遵从UIDynamicItem协议的对象都可以添加行为。UIView及其子类(包括UIControl)遵从这个协议,这意味着屏幕上可见的任何东西几乎都可以关联一种行为。

这里还有一件有意思的事情,UICollectionViewLayoutAttributes遵从UIDynamicItem协议,这样可以让集合视图的元素有动力学行为。默认的信息应用就是一个例子。

2.2.2 UIMotionEffect

物理拟物还没完。主屏幕上的视差效果,警告框好像浮动在视图上方的效果,Safari新的切换选项卡UI在倾斜设备时会显示“更多”内容,这些都是真实行为的模拟。用UIMotionEffect很容易就能为应用添加这类特性。iOS 7中的一个新类UIInterpolatingMotionEffect,能让开发者不费吹灰之力就添加这类效果。可以把UIMotionEffect理解为跟CAAnimation类似。CAAnimation会对其所属的层做动画,UIMotionEffect会对其所属的视图做动画。CAAnimation的动画是时间的函数,UIMotionEffect的动画则是设备动作的函数。我们会在第19章详细介绍UIKit Dynamics和UIMotionEffect。


CAAnimation动画是时间的函数,UIMotionEffect是设备动作的函数。


2.3 着色

UIView有了一个新属性,叫做tintColor,可以给应用着色。所有作为其他视图的子视图存在的UI元素都会在未设置tintColor的情况下使用父视图的tintColor。这意味着给应用程序的窗口设置tintColor就可以得到全局的着色。

这里要注意的很重要的一点是当模态化视图出现时,iOS 7会将其背后所有的UI元素变模糊。如果开发者有一个自定义视图,并且用了父视图的tintColor来做一些自定义渲染,就要覆盖tintColorDidChange方法来更新变化。


也可以在Xcode的故事板编辑器的File inspector面板中设置tintColor


2.4 用半透明实现层次和上下文

UIKit Dynamics和UIMotionEffect能帮助用户理解应用中的空间深度。iOS 7中还有一个强化空间深度概念的特性是在大部分模态化窗口上统一使用了模糊和半透明效果。控制中心和通知中心用一种精细的方式模糊了背景,以便用户能知道背后是什么。

iOS 7通过直接读取显存来实现这种效果。遗憾的是,iOS 7没有提供SDK来让开发者轻松实现这种效果,可能是出于安全上的考虑。比如说,无法这么做:

self.view.blurRadius = 50.0f;
self.view.saturationDelta = 2.0f;

 

不过,有一个WWDC讲座中的示例代码有UIImage的分类,UIImage+ImageEffects(本书的示例代码中有)允许实现这种效果,尽管这种方法很难用也不直观。但是你想超越极限,这也是你阅读本书的原因,不是吗?所以,在自己的应用中实现这种效果吧。

现在要展示如何创建一个看起来像通知中心或者控制中心背景的图层。首先创建一个单视图应用,挑一张漂亮的背景图加上,再添加一个按钮。接下来写一个按钮动作处理方法,弹出一个会将后面的背景模糊的视图。

捷径(或者说投机取巧)是创建一个不可见的UIToolbar并使用它的图层,而不是创建新图层。不过这种技巧很容易失效,所以不推荐。更好的办法是用iOS 7新的UIScreenshotting接口截屏,切割截屏图片,为其添加模糊效果,并把模糊的图片作为弹出视图的背景。

来看看代码:

UIImage+ImageEffects创建模糊的弹出层(SCTViewController.m)

// 创建图层**
self.layer = [CALayer layer];
self.layer.frame = CGRectMake(80, 100, 160, 160);
[self.view.layer addSublayer:self.layer];

**// 截屏**
float scale = [UIScreen mainScreen].scale;
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, scale);
[self.view drawViewHierarchyInRect:self.view.frame afterScreenUpdates:NO];
__block UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

**// 裁剪截图**
CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage,
   CGRectMake(self.layer.frame.origin.x * scale,
              self.layer.frame.origin.y * scale,
              self.layer.frame.size.width * scale,
              self.layer.frame.size.height * scale));
image = [UIImage imageWithCGImage:imageRef];

**// 添加效果**
image = [image applyBlurWithRadius:50.0f
                         tintColor:
[UIColor colorWithRed:0 green:1 blue:0 alpha:0.1]
             saturationDeltaFactor:2
                         maskImage:nil];
// 放到新建的图层上
self.layer.contents = (__bridge id)(image.CGImage);

 

applyBlurWithRadius:tintColor:saturationDeltaFactor:maskImage:方法位于UIImage+ImageEffects分类中,包括分类在内的完整代码可以从本书网站上下载。

图2-1显示了代码运行的效果。

【插入P25图】

图2-1 应用在iOS 7中运行时弹出视图前后的截屏

2.5 动态字体

在iOS 7中,用户可以设置文字大小。内置的邮件、日历以及其他大部分应用都会按照这个设置动态改变文字大小。注意增大文字大小并不总是能让字体的磅数变大。当文字大小设置的值可能会让小号字体无法辨认时,iOS 7会自动加粗文字。为应用增加动态字体支持后,还可以在设置应用中动态改变文字大小。不过,iOS 7的动态字体有一个缺点:在写作本章时,还不支持自定义字体。第21章会介绍动态字体。

2.6 自定义过渡效果

iOS 7的另一个新特性是苹果在大部分内置应用中尽量减少了屏幕切换的使用。打开日历应用,点击一个日期,日期视图以自定义的过渡动画效果从月视图中出现。类似地,创建新事件时,改变事件的开始和结束时间也是用动画实现自定义过渡。注意,这个动画在之前版本的iOS中是导航控制器的默认推入页面操作动画。还有一个例子是照片应用中的照片选项卡,万年不变的相机胶卷被一个使用自定义过渡在年度视图、精选视图、时刻视图和单张照片视图之间切换的集合视图替代了。iOS 7新的UI范式强调让用户知道自己在哪里,而不是让他们迷失在推入的无数视图控制器中。大部分情况下,这些动画都用自定义过渡实现,如图2-2所示。

【插入P26图】

图2-2 日历应用的屏幕,显示使用自定义过渡的屏幕

为iOS 7设计应用时,要认真考虑使用自定义过渡是否对于让用户知道自己在哪儿有所帮助。iOS 7 SDK增加了接口,能毫无困难地实现这些动画。

2.6.1 自定义过渡类型

iOS 7的SDK支持两类自定义过渡:自定义视图控制器过渡和交互式视图控制器过渡。自定义视图控制器过渡之前也能实现,要用到故事板和自定义联线。交互式视图控制器过渡则让用户利用手势(通常是拖动)控制过渡的过程(从开始到结束)。因此当用户拖动或快速滑动手指时,会从一个视图控制器过渡到另一个。

当过渡是时间的函数时,通常是自定义视图控制器过渡,是手势识别器的参数或类似事件的函数时,通常是交互式视图控制器过渡。

比如,可以认为导航控制器的推入过渡(就像iOS 6那样)是一种自定义视图控制器过渡,而UIPageViewController则是交互式视图控制器过渡。使用UIPageViewController在视图之间翻页时,过渡跟时间无关,页面的过渡跟着手指的移动走,所以这是交互式视图控制器过渡。而UINavigationController过渡(在iOS 6中)在一段时间内发生,所以这类过渡是自定义视图控制器过渡。

iOS 7 SDK允许开发者自定义几乎任何类型的过渡:视图控制器的presentation和dismissal、UINavigationController的推入和弹出过渡、UITabbarController的过渡(UITabbarController默认不对视图控制器做动画),甚至是集合视图的布局变化过渡。

自定义视图控制器过渡比交互式视图控制器过渡更易使用。本章会展示如何创建自定义视图控制器过渡。交互式视图控制器过渡相对较难使用,第20章会介绍这个主题。

2.7 把应用过渡(迁移)到iOS 7

到目前为止,本章介绍了iOS 7中引入的主要UI范式并实现了其中一种:模糊效果。在本节中,我们会展示如何把应用从iOS 6过渡到iOS 7。

你已经知道了,iOS 7的UI和之前的版本大不相同。因此,你得了解大量的API变化。可能的话,完全放弃对iOS 6的支持,因为iOS 7的升级率要比iOS 6高得多。不过,如果业务需要支持iOS 6,继续阅读下一节,我们会展示如何在不牺牲iOS 6的情况下支持iOS 7。

2.7.1 UIKit变化

在iOS 7中,几乎所有的UI元素都发生了变化。按钮没有边框,开关和分段控件变小了,滑块和进度条变细了。如果使用自动布局,大部分变化都不会对你有什么影响。如果没有使用自动布局,那么是时候把nib文件升级到自动布局了。如果不使用自动布局,那么在3.5寸和4寸设备上支持iOS 6和iOS 7就需要写大量的布局代码。

如果你之前一直不想使用自动布局,那么现在必须开始学习了。Xcode 5中的自动布局要比Xcode 4.x中的好用得多,本书第6章会更为详细地讲解自动布局。

2.7.2 自定义设计

一般来说大部分应用都会用自定义设计图来做应用皮肤,开发者需要仔细考虑如何设计。原因是iOS 7的设计是非常“平”的,视觉拟物削弱了很多。这么做是因为在iPhone 4之前,几乎所有的屏幕(手机/PC/Mac)像素密度都在70 ppi~160 ppi(每英寸像素数)之间。iPhone 4的视网膜屏将像素密度增加到了320 ppi。这个像素密度接近健康人眼所能接受的最高程度,再多就是人眼接受不到的多余数据了。这也是印刷业采用300 ppi的原因。

印刷业不需要模拟抛光发亮的按钮、不规则边框、渐变或者艺术字体。你什么时候见过广告牌或者杂志封面的图上加上人造抛光或者发亮的效果?没有。为什么?因为不需要。(有些杂志有光泽,但那是真的光泽。)事实上,软件设计师利用抛光、发亮按钮来让他们的设计在(当时的)低分辨率(70 ppi~160 ppi)屏幕上看起来比较好看。

而iPhone 4屏幕的像素密度达到了接近高印刷质量的杂志的程度,为UI添加光泽和发光效果就没有必要了。这就是Twitterrific 5、LetterPress和Clear(代办事项应用)甚至在苹果之前就大量减少了界面效果的原因。Windows Phone 7界面从第一天开始就没有抛光、发亮或者阴影。

今天,除了iPad mini,苹果在2011年和2012年间发布的几乎所有iOS设备都采用了视网膜屏。扁平化设计是未来。给UI设计光泽在iOS 7中看起来就过时了。

如果用了抛光或是发亮按钮,你就得重新设计UI了。问问自己,这个UI在杂志上打印出来会是什么样子的。你得像印刷业的设计师那样考虑问题,而不是软件业的设计师。

2.7.3 支持iOS6

写向后兼容iOS 6的代码要比以前难,因为iOS 7是其诞生以来的一次大升级。如果你的应用需要支持iOS 6,首先让iOS 6的应用跟iOS 7的外观类似,这意味着并非要在iOS 7中用扁平按钮却在iOS 6中用抛光按钮,而是把iOS 6的按钮也变扁平。首先改变iOS 6版本的设计,使其看起来像iOS 7版本。对结果满意后,再添加iOS 7独有的特性。

1. 应用图标

iOS 7使用的图标大小和iOS 6不同,对于iPhone应用是120×120,对于iPad应用是152×152。你得为应用更换这些略大的图标,避免使用抛光或者发亮,“Icon already includes gloss effects (UIPrerenderedIcon)”这个设置在iOS 7下不起作用,可能最终会废弃。

2. 启动图

启动图现在应该是480点高(对于iPhone 5来说是568)。iOS 7中启动图会渲染在状态栏下面,如果你用的启动图太短,屏幕底部会出现黑边。

3. 状态栏

iOS 7中的状态栏是半透明的,会将其背后的内容模糊。在iOS 6版本的应用中,应该用抛光不那么多的设计图(甚至完全没有光泽)确保状态栏是扁平的。在iOS 7中,视图控制器会延伸到全屏,位于状态栏下面。在iOS 6版本的应用中,可以把状态栏的样式改为UIStatusBarStyleBlackTranslucent来模拟这种行为。

4. 导航栏

iOS 7中的导航栏默认也是半透明的,而且没有阴影,底部边界倒是有一条细线。可以考虑为iOS 6版本的应用添加类似的效果来取代阴影。

在iOS 6中,设置tintColor会改变导航栏的颜色,要在iOS 7中得到同样的效果,需要设置barTintColor。导航栏上的返回按钮也变了,以前是带边框的按钮,现在则是箭头和文字。记住,就算是默认按钮也不带边框了,而且UIButtonTypeRoundedRect也废弃了。在iOS 6版本的应用中,可以用导航栏的外观代理协议(appearance proxy protocol )来自定义返回按钮,并为返回按钮设置类似于iOS 7版本的图片。

导航栏的背景图通常是320×44,有时候开发者可能会用略高的带阴影的导航栏。在iOS 7中,导航栏的背景图延伸到了状态栏下方,而不是像iOS 6那样在视图控制器上方。避免使用高于44点的导航栏背景图。你可能得重新设计背景图并且考虑用别的办法添加阴影。iOS 6为UINavigationBar引入了shadowImage属性,可以用这个属性设置导航栏阴影。

5. 工具栏

工具栏也是半透明的,不过更重要的是工具栏上的按钮没有边框。如果按钮不多于3个,可以考虑用文本按钮而不是图片按钮。比如iOS 7的音乐应用中的正在播放页面用文本按钮表示重复、随机和创建(如图2-3所示)。

【插入P29图1】

图2-3 iOS 7中控制按钮的截图

6. 视图控制器

在iOS 7中,所有视图控制器都是全屏布局的。可以在iOS 7中支持新布局,在iOS 6中保留旧布局,但是应用看起来会有点过时。要拥抱变化,在iOS 6版本中用全屏布局。将视图控制器的wantsFullscreenLayout属性设置成YES就可以在iOS 6中实现这一点。注意,这个属性在iOS 7中废弃了,而且在iOS 7中将这个属性设置成NO的行为是未定义的。

在iOS 7中也可以使用不透明的条栏,这可以通过Interface Builder的选项控制视图在半透明/不透明条栏下方的样子,如图2-4所示。

【插入P29图2】

图2-4 Interface Builder中将视图延伸到半透明条栏下方的选项

Interface Builder的nib文件中还有Top Layout Guide和Bottom Layout Guide(只有打开自动布局才有),也可以用这些引导点设置约束条件。所以开发者可以让按钮总是离Top Layout Guide50个点。

7. 表格视图控制器

表格视图控制器,尤其是分组表格视图样式,不再有内嵌效果了。分割线变成了内嵌的,从图片开始,到屏幕边缘结束。单元分割线变细了,颜色也变浅了。

section头颜色也变浅了,而且是纯色,不再是渐变色了。在iOS 7版本的应用中可以给sectionIndexBackgroundColor设置值来改变颜色。在iOS 6中,只能在tableView:viewForHeaderInSection:委托方法中返回外观和iOS 7版本类似的视图。

还有一个重要的变化是选择样式。iOS 6提供了两种样式:蓝色和灰色。用内置样式时,文本的前景色会从黑色变成白色。在iOS 7中不会这样,高亮色只是一种浅灰色,覆盖UITableViewCell子类的setSelected:animated:setHighlighted:animated:可以模拟这种行为。

默认的滑动删除手势以前是从左向右滑,现在是从右向左滑。如果开发者正考虑在表格视图单元中用从右向左滑的手势来显示菜单或做一个动作,就得考虑用别的方法来实现了。

8. 拖动手势

iOS 7默认在所有应用中使用两种拖动手势,第一种是UIScreenEdgePanGestureRecognizer,在导航控制器中,从屏幕边缘滑动可以让用户回到上一个视图。这个行为是默认的。如果你在使用像Facebook的旧版应用那样的汉堡菜单(俗称侧滑菜单或者侧滑面板),就得考虑关掉显示菜单的手势。事实上,随着iOS 7的发布,Facebook完全抛弃了汉堡菜单,采用了选项卡栏。第二种手势是从屏幕底部拖动的手势,可以显示控制中心。如果你的应用使用了类似的手势来调用某个功能,就得重新设计界面了。

9. 警告框和操作列表

警告框和操作列表总是使用系统的默认样式,除非开发者创建自己的。如果有自定义的警告框,可以考虑加入UIMotionEffect以便让其看起来像是浮在视图控制器上方。在iOS 6中建立层次概念要靠阴影,而在iOS 7中则用运动效果。在iOS 6中模拟运动效果很难。除非愿意花费时间和精力,否则就在iOS 6中保留UI元素的阴影吧。

2.8 小结

本章介绍了新的UI范式,还有新设计背后的深层次原因,并且深入讲解了iOS 7中不同的UI相关的技术。最后,我们介绍了如何将应用迁移到iOS 7,并且(可选地)维持向后兼容性。要做到写出在iOS 6和iOS 7上都能工作的代码并且外观在两个系统上看起来都很好是很难的(至少是相对而言)。如果业务允许你投入时间和资源,那就做吧。否则,把精力集中在iOS 7每个可能的功能上,让应用脱颖而出。

iOS 7为开发者提供了在App Store大获成功的机遇,举几个例子:iOS 7专用的待办事项应用,iOS 7专用的Twitter客户端,iOS 7专用的日历应用。理解了iOS 7的UI范式,是时候大干一把了。

posted @ 2020-02-03 16:56  Julday  阅读(446)  评论(0编辑  收藏  举报