1、基本概念
在 iPad 和 iPhone 5 出现之前,iOS 设备就只有一种尺寸。我们在做屏幕适配时需要考虑的仅仅有设备方向而已。而很多应用并不支持转向,这样的话就完全没有屏幕适配的工作了。随着 iPad 和 iPhone 5,以及接下来的 iPhone 6 的推出,屏幕尺寸也变成了需要考虑的对象。在 iOS 7 之前,为一个应用,特别是 universal 的应用制作 UI 时,我们总会首先想我们的目标设备的长宽各是多少,方向变换以后布局又应该怎么改变,然后进行布局。iOS 6 引入了 Auto Layout 来帮助开发者使用约束进行布局,这使得在某些情况下我们不再需要考虑尺寸,而可以专注于使用约束来规定位置。
既然我们有了 Auto Layout,那么其实通过约束来指定视图的位置和尺寸是没有什么问题的了,从这个方面来说,屏幕的具体的尺寸和方向已经不那么重要了。但是实战中这还不够,Auto Layout 正如其名,只是一个根据约束来进行布局的方案,而在对应不同设备的具体情况下的体验上还有欠缺。一个最明显的问题是它不能根据设备类型来确定不同的交互体验。很多时候你还是需要判断设备到底是 iPhone 还是 iPad,以及现在的设备方向究竟是竖直还是水平来做出判断。这样的话我们还是难以彻底摆脱对于设备的判断和依赖,而之后如果有新的尺寸和设备出现的话,这种依赖关系显然显得十分脆弱的(想想要是有 iWatch 的话..)。
所以在 iOS 8 里,Apple 从最初的设计哲学上将原来的方式推翻了,并引入了一整套新的理念,来适应设备不断的发展。这就是 Size Classes。
不再根据设备屏幕的具体尺寸来进行区分,而是通过它们的感官表现,将其分为普通 (Regular) 和紧密 (Compact) 两个种类 (class)。开发者便可以无视具体的尺寸,而是对这这两类和它们的组合进行适配。这样不论在设计时还是代码上,我们都可以不再受限于具体的尺寸,而是变成遵循尺寸的视觉感官来进行适配。
Size Classes有三个值:Regular,Compact 和Any。Any是什么意思呢?如果weight设为Any,height设置为Regular,那么在该状态下的界面元素在只要height为Regular,无论weight是Regular还是Compact的状态中都会存在。这种关系应该叫做继承关系,具体的四种界面描述与可继承的界面描述如下:
w:Compact h:Compact 继承 (w:Any h:Compact , w:Compact h:Any , w:Any h:Any)
w:Regular h:Compact 继承 (w:Any h:Compact , w:Regular h:Any , w:Any h:Any)
w:Compact h:Regular 继承 (w:Any h:Regular , w:Compact h:Any , w:Any h:Any)
w:Regular h:Regular 继承 (w:Any h:Regular , w:Regular h:Any , w:Any h:Any)
这么多设备(iPhone4S,iPhone5/5s,iPhone6,iPhone6 Plus,iPad,Apple Watch)的尺寸,就通过Size Classes简单的表达出来了:
- iPhone4S,iPhone5/5s,iPhone6
- 竖屏:(w:Compact h:Regular)
- 横屏:(w:Compact h:Compact)
- iPhone6 Plus
- 竖屏:(w:Compact h:Regular)
- 横屏:(w:Regular h:Compact)
- iPad
- 竖屏:(w:Regular h:Regular)
- 横屏:(w:Regular h:Regular)
- Apple Watch(猜测)
- 竖屏:(w:Compact h:Compact)
- 横屏:(w:Compact h:Compact)
PS:附上图形:
2、UITraitCollection 和 UITraitEnvironment (Size Classes手写代码)
为了表征 Size Classes,Apple 在 iOS 8 中引入了一个新的类,UITraitCollection
。这个类封装了像水平和竖直方向的 Size Class 等信息。iOS 8 的 UIKit 中大多数 UI 的基础类 (包括UIScreen
,UIWindow
,UIViewController
和 UIView
) 都实现了 UITraitEnvironment
这个接口,通过其中的 traitCollection
这个属性,我们可以拿到对应的 UITraitCollection
对象,从而得知当前的 Size Class,并进一步确定界面的布局。
和 UIKit 中的响应者链正好相反,traitCollection
将会在 view hierarchy 中自上而下地进行传递。对于没有指定 traitCollection
的 UI 部件,将使用其父节点的 traitCollection
。这在布局包含 childViewController 的界面的时候会相当有用。在 UITraitEnvironment
这个接口中另一个非常有用的是 -traitCollectionDidChange:
。在 traitCollection
发生变化时,这个方法将被调用。在实际操作时,我们往往会在 ViewController 中重写 -traitCollectionDidChange:
或者 -willTransitionToTraitCollection:withTransitionCoordinator:
方法 (对于 ViewController 来说的话,后者也许是更好的选择,因为提供了转场上下文方便进行动画;但是对于普通的 View 来说就只有前面一个方法了),然后在其中对当前的 traitCollection
进行判断,并进行重新布局以及动画。代码看起来大概会是这个样子:
override func willTransitionToTraitCollection(newCollection: UITraitCollection, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator){ super.willTransitionToTraitCollection(newCollection, withTransitionCoordinator: coordinator) coordinator.animateAlongsideTransition({ (context: UIViewControllerTransitionCoordinatorContext!) -> Void in if (newCollection.verticalSizeClass == UIUserInterfaceSizeClass.Compact) { //To Do: modify something for compact vertical size } else { //To Do: modify something for other vertical size } self.view.setNeedsLayout() }, completion: nil) }
在两个 To Do 中,我们应该删除或者添加或者更改不同条件下的 Auto Layout 约束 (当然,你也可以干其他任何你想做的事情),然后调用 -setNeedsLayout
来在上下文中触发转移动画。如果你坚持用代码来处理的话,可能需要面临对于不同 Size Classes 来做移除旧的约束和添加新的约束这样的事情,可以说是很麻烦 (至少我觉得是麻烦的要死)。但是如果我们使用 IB 的话,这些事情和代码都可以省掉,我们可以非常方便地在 IB 中指定各种 Size Classes 的约束 (稍后会介绍如何使用 IB 来对应 Size Classes)。另外使用 IB 不仅可以节约成百上千行的布局代码,更可以从新的 Xcode 和 IB 中得到很多设计时就可以实时监视,查看并且调试的特性。可以说手写 UI 和使用 IB 设计的时间消耗和成本差距被进一步拉大,并且出现了很多手写 UI 无法实现,但是 IB 可以不假思索地完成的任务。从这个意义上来说,新的 IB 和 Size Classes 系统可以说无情地给手写代码判了个死缓。
另外,新的 API 和体系的引入也同时给很多我们熟悉的 UIViewController 的有关旋转的老朋友判了死刑,比如下面这些 API 都弃用了:
@property(nonatomic, readonly) UIInterfaceOrientation interfaceOrientation
- willRotateToInterfaceOrientation:duration:
- willAnimateRotationToInterfaceOrientation:duration:
- didRotateFromInterfaceOrientation:
- shouldAutomaticallyForwardRotationMethods
现在全部统一到了 viewWillTransitionToSize:withTransitionCoordinator:
,旋转的概念不再被提倡使用。其实仔细想想,所谓旋转,不过就是一种 Size 的改变而已,我们都被 Apple 骗了好多年,不是么?
3、Interface Builder 中使用 Size Classes
创建一个新的通用项目。如果你想要早在一个已经创建了的Xcode6项目,你需要激活size classes选项。你可以在Interface Builder中的属性面板 勾选autolayout 的选项的下面找到它。
首先,让我们在Xcode中看一下size class的网格。这是一个你可以在不同的布局排列间切换的区域。当你查看storyboard的时候,看到视图的底部,并且点击‘wAny hAny’字样的标签。你将会看到一些类似网格的画面。
默认的,我们以一个基础的设置开始,也就是any width和any height。很多事情都将在这里安置和改变,包括了iphone和ipad的所有方向的默认布局。苹果建议把大多数的设置都在这个界面中进行设置。这个是因为减少工作量而显得特别的简单。让我们布局一个超级宽的按钮在画面的中间。给它一个绿色的背景,从而让我们看到它真实的尺寸,给它一个约束来让他居中。
并且给它一个夸张的固定宽度600。
好了,现在在ipad和iphone的模拟器都运行一下,你将会看到都是居中,但对于iphone的两个方向都太宽了,(这里你设置了页面中button的宽度 但并没有马上更新是因为 你在做添加约束的时候没有更新图形,导致了如下图的情况,storyboard里面没有更新,而在模拟器运行时候更新了,左边大纲栏目里面也有警告说明,可以直接点击警告里面的黄色三角来更新画面其实就是 Updata Frame)
让我们使用size classes来修正吧。回到刚才那个第一张图的网格选择iphone的纵向(portrait)设置,就是紧凑的宽度+ 常规的高度。网格中的红色矩形。
你将会注意到你在网格中选中之后底部的bar改变为蓝色。那是在警告你:“Hey ,你并不是在一个基础的设置,有些改变将会只在你运行的时候显示。所以这个bar现在是蓝色的!” 我所说的一些改变是因为有四项你能改变的size classes:1约束常数,2字体,3约束的开/关,4子视图的开/关。
前两个是不言而喻的,但是让我来告诉你如何让后两者工作。在当前的size class (compact width 和 regular height)状况下让我们试着把一个约束关闭。在文档的提纲栏里,点击设置在我们的button的Centre X 校准约束:
现在看一下我们的属性检查栏,在底部我们可以看到带标记的一个单词“Installed”,并且左侧有额外的加号按钮。点击额外的加号并且点选'Compact Width| Regular Height'(当前的就是)。
现在你将会看到2个标记物,把刚刚添加的哪一个取消勾选(wC hR)
现在我们的约束不再安置并且做任何事情来配置size classes。就像你看到的,Xcode正在控诉我们的约束太混乱了(左边的大纲会有错误提示表示你缺少了约束-译者),如果你这时候运行app在iphone的模拟器上的话,按钮不在X方向居中了。但是在ipad的上面还是居中的,因为约束仍然安置在基本的设置里面。这个约束将会一直配置着除非我们把它取消勾选。你甚至能够旋转你的iphone模拟器,并且发现button将会神奇的回到居中,因为iphone的横向是不同的size class配置,好了,让我们把勾选回来,让button回到居中。
现在让我们改变我们设置在button宽度的约束,选择button,并且来到Size的属性检查栏,下拉到底部,我们可以看到所有的约束。点击Width原本是600的 使用Edit设置为100:
在iPhone的模拟器上运行,你将会看到button已经具备了正确的宽度。运行在ipad的模拟器的时候却展示了600的宽度,因为我们没有改变基本设置里面的宽度。但是,在iphone的横向landscape仍然看着不怎么样,因为iphone的横向设置来自基本的Any Any 的设置。让我们修正一下。在网格里面我们选择compact Width和Compact Height。也就是第一张图的蓝色网格。
现在我们在这个设置下改变width 的约束,就像我们为了compact x regular改变的一样。给予一个400的宽度。运行一下iphone的模拟器,并且旋转到横向,按钮有了400的宽度,看上去很棒。达到了我们的预想。有一点很好就是你能看到一个所有的约束的列表,这些都是不同的设置的。仅仅选择你想要在文档大纲里面看到的约束,然后来到属性检查栏,他们整齐的排列在初始的常数下面。它标注了每一个基于它所应用的设置。
即使我们决定我们想要只在iphone横向landscape模式下button消失,使用size classes 我们只要反向安置views就像我们反向安置一个约束。选择我们的UIbutton,滚动到属性检查器的底部。通过点击 加号按钮 给我们当前的设置添加一个新的安置选项,然后取消勾选它。
就像你看到的,那个view 立马消失了,因为我们在设置里面反向安置了它,我们立马就能看到。运行app,你能看到它在纵向的portrait iphone上消失了,但是当你旋转到横向的landscape的时候又回来了。当然它也一直安置在ipad上面因为ipad仍然使用的是基本的设置。
4、Size Classes 和 Image Asset 及 UIAppearence
Image Asset 里也加入了对 Size Classes 的支持,也就是说,我们可以对不同的 Size Class 指定不同的图片了。在 Image Asset 的编辑面板中选择某张图片,Inspector 里现在多了一个 Width 和 Height 的组合,添加我们需要对应的 Size Class, 然后把合适的图拖上去,这样在运行时 SDK 就将从中挑选对应的 Size 的图进行替换了。不仅如此,在 IB 中我们也可以选择对应的 size 来直接在编辑时查看变化。
实际做起来实在是太简单了..但拿个 demo 说明一下吧,比如下面这个实现了竖直方向 Compact 的时候将笑脸换成哭脸 -- 当然了,一行代码都不需要。
另外,在 iOS 7 中 UIImage 添加了一个 renderingMode
属性。我们可以使用imageWithRenderingMode:
并传入一个合适的 UIImageRenderingMode
来指定这个 image 要不要以Template 的方式进行渲染。在新的 Xcode 中,我们可以直接在 Image Asset 里的 Render As
选项来指定是不是需要作为 template 使用。而相应的,在 UIApperance
中,Apple 也为我们对于 Size Classes 添加了相应的方法。使用 +appearanceForTraitCollection:
方法,我们就可以针对不同 trait 下的应用的 apperance 进行很简单的设定。比如在上面的例子中,我们想让笑脸是绿色,而哭脸是红色的话,不要太简单。首先在 Image Asset 里的渲染选项设置为 Template Image
,然后直接在 AppDelegate
里加上这样两行:
UIView.appearanceForTraitCollection(UITraitCollection(verticalSizeClass:.Compact)).tintColor = UIColor.redColor()
UIView.appearanceForTraitCollection(UITraitCollection(verticalSizeClass:.Regular)).tintColor = UIColor.greenColor()
完成,只不过拖拖鼠标,两行简单的代码,随后还能随喜换色,果然是大快所有人心的大好事。
感谢:
http://www.cnblogs.com/fengquanwang/p/3998526.html
http://onevcat.com/2014/07/ios-ui-unique/
http://www.cnblogs.com/wfwenchao/p/4015333.html
http://www.cocoachina.com/ios/20140926/9766.html
相关推荐
Prepare visual layouts for an iOS application using storyboards, size classes, and auto-layout Integrate many common technologies into an app, such as multi-touch gestures, CoreData, and notifications...
OS X app not launching with the correct size Chapter 29. Detecting the application target Chapter 30. Disabling a method Chapter 31. Deprecating a method Chapter 32. Xcode "beachballs" when opening a...
It covers essential topics such as device orientation, size classes, and multitasking modes, providing developers with the knowledge needed to create responsive and adaptive user interfaces....
Prepare visual layouts for an iOS application using storyboards, size classes, and auto-layout Integrate many common technologies into an app, such as multi-touch gestures, CoreData, and notifications...
6. **Interface Builder插件**:Xcode的Interface Builder提供了丰富的插件支持,如_IBInspectable和_IBDesignable,它们让开发者可以在Storyboard中实时预览和调整自定义控件的属性。 7. **...
Prepare visual layouts for an iOS application using storyboards, size classes, and auto-layout Integrate many common technologies into an app, such as multi-touch gestures, CoreData, and notifications...
可以利用Auto Layout或Size Classes来实现布局的自动调整。 6. **事件处理**:当用户点击菜单项时,需要有相应的响应。这通常涉及到UIControl的事件处理,如 addTarget(_:action:for:) 方法。 7. **触摸检测**:...
views and view controllers, and with a knowledge of the language and the Xcode IDE already presupposed. If you started reading Programming iOS 9 and wondered about such unexplained matters as Swift ...
4. **Auto Layout与Size Classes**:了解如何使用Auto Layout和Size Classes来实现界面的自动布局,确保应用在不同尺寸的设备上都能正常显示。 5. ** MVC 模式**:Model-View-Controller(MVC)是iOS开发中的主要...
The key new features of the iOS 8 SDK and Xcode 6 are also covered, including Swift playgrounds, universal user interface design using size classes, app extensions, Interface Builder Live Views, ...
总之,开发一个简单的手机游戏页面涉及的知识点包括:使用Swift编程,通过Xcode和Interface Builder构建UI,运用Auto Layout和Size Classes实现设备适配,利用UIView动画API创建动态效果,处理触摸事件以实现交互,...
本章还会介绍如何通过不同尺寸类(Size Classes)来适应不同屏幕尺寸,以及如何保护矢量数据。 第六章将目光转向原型设计。原型设计是定义应用构思和结构的重要阶段,包括在纸上草图、创建线框图、使草图/线框图...
7. **Auto Layout与Size Classes**:在iOS 11中,Auto Layout仍然是实现响应式界面的重要工具,Size Classes则帮助开发者适应不同设备尺寸。 8. **地图与定位服务**:讲解如何集成Core Location和MapKit,实现定位...
5. 尺寸类别:为了适应不同屏幕尺寸的设备,iOS使用尺寸类别(size classes)来定义设备的方向和屏幕空间的特征。尺寸类别可以是水平宽度的紧凑(Compact)、常规(Regular)或在竖屏模式下的iPhone,也可以是iPad在...
5. **Storyboard和Interface Builder**:Xcode的Interface Builder提供了可视化设计工具,可方便地设置约束和调整Size Classes,让开发者直观地看到布局效果。 在"Chapter 02 - Beginning Adaptive Layout"中,你将...
Additional features of iOS development using Xcode 7 are also covered, including Swift playgrounds, universal user interface design using size classes, app extensions, Interface Builder Live Views, ...
• Use Auto Layout and Size Classes to adapt to different screen sizes and orientations • Build advanced UIs with Tables, Split Views, Navigation Controllers, and more • Read and write preferences ...