zoukankan      html  css  js  c++  java
  • Xcode 6 AutoLayout Size Classes

    1、基本概念

    在iPad和iPhone 5出现之前,iOS设备就唯独一种尺寸。

    我们在做屏幕适配时须要考虑的唯独设备方向而已。

    而非常多应用并不支持转向,这种话就全然没有屏幕适配的工作了。

    随着iPad和iPhone 5。以及接下来的iPhone 6的推出,屏幕尺寸也变成了须要考虑的对象。在iOS7之前,为一个应用。特别是universal的应用制作UI时,我们总会首先想我们的目标设备的长宽各是多少。方向变换以后布局又应该怎么改变,然后进行布局。

    iOS6引入了AutoLayout来帮助开发人员使用约束进行布局,这使得在某些情况下我们不再须要考虑尺寸,而能够专注于使用约束来规定位置。

    既然我们有了AutoLayout,那么事实上通过约束来指定视图的位置和尺寸是没有什么问题的了。从这个方面来说,屏幕的详细的尺寸和方向已经不那么重要了。可是实战中这还不够。AutoLayout正如其名,仅仅是一个依据约束来进行布局的方案。而在相应不同设备的详细情况下的体验上还有欠缺。一个最明显的问题是它不能依据设备类型来确定不同的交互体验。非常多时候你还是须要推断设备究竟是iPhone还是iPad。以及如今的设备方向究竟是竖直还是水平来做出推断。

    这种话我们还是难以彻底摆脱对于设备的推断和依赖,而之后假设有新的尺寸和设备出现的话。这种依赖关系显然显得十分脆弱的(想想要是有iWatch的话..)。

    所以在iOS8里,Apple从最初的设计哲学上将原来的方式推翻了。并引入了一整套新的理念,来适应设备不断的发展。这就是SizeClasses。

    不再依据设备屏幕的详细尺寸来进行区分。而是通过它们的感官表现,将其分为普通(Regular)和紧密(Compact)两个种类(class)。开发人员便能够无视详细的尺寸,而是对这这两类和它们的组合进行适配。这样不论在设计时还是代码上。我们都能够不再受限于详细的尺寸,而是变成遵循尺寸的视觉感官来进行适配。

    SizeClasses有三个值:Regular,Compact和Any。

    Any是什么意思呢?假设weight设为Any,height设置为Regular,那么在该状态下的界面元素在仅仅要height为Regular,不管weight是Regular还是Compact的状态中都会存在。这样的关系应该叫做继承关系。详细的四种界面描写叙述与可继承的界面描写叙述例如以下:

    1
    2
    3
    4
    w:Compacth:Compact继承(w:Anyh:Compact,w:Compacth:Any,w:Anyh:Any)
    w:Regularh:Compact继承(w:Anyh:Compact,w:Regularh:Any,w:Anyh:Any)
    w:Compacth:Regular继承(w:Anyh:Regular,w:Compacth:Any,w:Anyh:Any)
    w:Regularh:Regular继承(w:Anyh:Regular,w:Regularh:Any,w:Anyh:Any)

    这么多设备(iPhone 4S,iPhone 5/5s,iPhone 6,iPhone 6Plus,iPad,AppleWatch)的尺寸。就通过SizeClasses简单的表达出来了:

    iPhone4S,iPhone 5/5s,iPhone 6

    竖屏:(w:Compacth:Regular)

    横屏:(w:Compacth:Compact)

    iPhone6Plus

    竖屏:(w:Compacth:Regular)

    横屏:(w:Regularh:Compact)

    iPad

    竖屏:(w:Regularh:Regular)

    横屏:(w:Regularh:Regular)

    AppleWatch(推測)

    竖屏:(w:Compacth:Compact)

    横屏:(w:Compacth:Compact)

    PS:附上图形:




    2、UITraitCollection和UITraitEnvironment(Size Classes手写代码)

    为了表征SizeClasses。Apple在iOS 8中引入了一个新的类。UITraitCollection。

    这个类封装了像水平和竖直方向的SizeClass等信息。iOS 8的UIKit中大多数UI的基础类(包含UIScreen。UIWindow。UIViewController和UIView)都实现了UITraitEnvironment这个接口。通过当中的traitCollection这个属性。我们能够拿到相应的UITraitCollection对象。从而得知当前的SizeClass,并进一步确定界面的布局。

    和UIKit中的响应者链正好相反。traitCollection将会在viewhierarchy中自上而下地进行传递。对于没有指定traitCollection的UI部件。将使用其父节点的traitCollection。这在布局包括childViewController的界面的时候会相当实用。

    在UITraitEnvironment这个接口中还有一个很实用的是-traitCollectionDidChange:。

    在traitCollection发生变化时,这种方法将被调用。在实际操作时。我们往往会在ViewController中重写-traitCollectionDidChange:或者-willTransitionToTraitCollection:withTransitionCoordinator:方法(对于ViewController来说的话,后者或许是更好的选择。由于提供了转场上下文方便进行动画;可是对于普通的View来说就仅仅有前面一个方法了),然后在当中对当前的traitCollection进行推断。并进行又一次布局以及动画。

    代码看起来大概会是这个样子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        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中,我们应该删除或者加入或者更改不同条件下的AutoLayout约束(当然,你也能够干其它不论什么你想做的事情),然后调用-setNeedsLayout来在上下文中触发转移动画。

    假设你坚持用代码来处理的话,可能须要面临对于不同SizeClasses来做移除旧的约束和加入新的约束这种事情,能够说是非常麻烦(至少我认为是麻烦的要死)。

    可是假设我们使用IB的话,这些事情和代码都能够省掉。我们能够非常方便地在IB中指定各种SizeClasses的约束(稍后会介绍怎样使用IB来相应SizeClasses)。另外使用IB不仅能够节约成百上千行的布局代码。更能够从新的Xcode和IB中得到非常多设计时就能够实时监视,查看而且调试的特性。能够说手写UI和使用IB设计的时间消耗和成本差距被进一步拉大,而且出现了非常多手写UI无法实现。可是IB能够不假思索地完毕的任务。从这个意义上来说。新的IB和SizeClasses系统能够说无情地给手写代码判了个死缓。

    另外,新的API和体系的引入也同一时候给非常多我们熟悉的UIViewController的有关旋转的老朋友判了死刑,比方以下这些API都弃用了:

    1
    2
    3
    4
    5
    6
    @property(nonatomic, readonly) UIInterfaceOrientation interfaceOrientation
     
    - willRotateToInterfaceOrientation:duration:
    - willAnimateRotationToInterfaceOrientation:duration:
    - didRotateFromInterfaceOrientation:
    - shouldAutomaticallyForwardRotationMethods

    如今所有统一到了viewWillTransitionToSize:withTransitionCoordinator:,旋转的概念不再被提倡使用。事实上细致想想。所谓旋转,只是就是一种Size的改变而已,我们都被Apple骗了好多年,不是么?

    3、InterfaceBuilder中使用SizeClasses

    创建一个新的通用项目。假设你想要早在一个已经创建了的Xcode6项目,你须要激活sizeclasses选项。你能够在InterfaceBuilder中的属性面板勾选autolayout的选项的以下找到它。


    首先。让我们在Xcode中看一下sizeclass的网格。这是一个你能够在不同的布局排列间切换的区域。

    当你查看storyboard的时候,看到视图的底部,而且点击‘wAnyhAny’字样的标签。

    你将会看到一些类似网格的画面。


    默认的,我们以一个基础的设置開始,也就是anywidth和anyheight。

    非常多事情都将在这里安置和改变,包含了iphone和ipad的全部方向的默认布局。苹果建议把大多数的设置都在这个界面中进行设置。

    这个是由于降低工作量而显得特别的简单。让我们布局一个超级宽的button在画面的中间。

    给它一个绿色的背景,从而让我们看到它真实的尺寸,给它一个约束来让他居中。


    而且给它一个夸张的固定宽度600。

    9.png

    好了,如今在ipad和iphone的模拟器都执行一下。你将会看到都是居中,但对于iphone的两个方向都太宽了,(这里你设置了页面中button的宽度但并没有立即更新是由于你在做加入约束的时候没有更新图形,导致了例如以下图的情况,storyboard里面没有更新,而在模拟器执行时候更新了,左边大纲栏目里面也有警告说明,能够直接点击警告里面的黄色三角来更新画面事实上就是UpdataFrame)


    让我们使用sizeclasses来修正吧。

    回到刚才那个第一张图的网格选择iphone的纵向(portrait)设置,就是紧凑的宽度+常规的高度。网格中的红色矩形.

    11.png

    你将会注意到你在网格中选中之后底部的bar改变为蓝色。那是在警告你:“Hey,你并非在一个基础的设置。有些改变将会仅仅在你执行的时候显示。所以这个bar如今是蓝色的!”我所说的一些改变是由于有四项你能改变的sizeclasses:1约束常数。2字体。3约束的开/关,4子视图的开/关。

    前两个是不言而喻的,可是让我来告诉你怎样让后两者工作。在当前的sizeclass(compactwidth和regularheight)状况下让我们试着把一个约束关闭。在文档的提纲栏里,点击设置在我们的button的CentreX校准约束:

    12.png

    如今看一下我们的属性检查栏,在底部我们能够看到带标记的一个单词“Installed”,而且左側有额外的加号button。点击额外的加号而且点选'CompactWidth|RegularHeight'(当前的就是)。

    如今你将会看到2个标记物,把刚刚加入的哪一个取消勾选(wChR)

    13.png

    如今我们的约束不再安置而且做不论什么事情来配置sizeclasses。

    就像你看到的,Xcode正在控诉我们的约束太混乱了(左边的大纲会有错误提示表示你缺少了约束-译者)。假设你这时候执行app在iphone的模拟器上的话,按钮不在X方向居中了。

    可是在ipad的上面还是居中的。由于约束仍然安置在主要的设置里面。这个约束将会一直配置着除非我们把它取消勾选。

    你甚至可以旋转你的iphone模拟器,而且发现button将会奇妙的回到居中,由于iphone的横向是不同的sizeclass配置。好了,让我们把勾选回来。让button回到居中。

    如今让我们改变我们设置在button宽度的约束,选择button。而且来到Size的属性检查栏,下拉究竟部,我们能够看到全部的约束。点击Width原本是600的使用Edit设置为100:

    14.png

    在iPhone的模拟器上执行,你将会看到button已经具备了正确的宽度。

    执行在ipad的模拟器的时候却展示了600的宽度,由于我们没有改变基本设置里面的宽度。可是,在iphone的横向landscape仍然看着不怎么样。由于iphone的横向设置来自主要的AnyAny的设置。

    让我们修正一下。

    在网格里面我们选择compactWidth和CompactHeight。也就是第一张图的蓝色网格。

    如今我们在这个设置下改变width的约束,就像我们为了compactxregular改变的一样。给予一个400的宽度。

    执行一下iphone的模拟器。而且旋转到横向。button有了400的宽度,看上去非常棒。

    达到了我们的预想。有一点非常好就是你能看到一个全部的约束的列表。这些都是不同的设置的。只选择你想要在文档大纲里面看到的约束,然后来到属性检查栏。他们整齐的排列在初始的常数以下。它标注了每个基于它所应用的设置。

    即使我们决定我们想要仅仅在iphone横向landscape模式下button消失。使用sizeclasses我们仅仅要反向安置views就像我们反向安置一个约束。选择我们的UIbutton,滚动到属性检查器的底部。

    通过点击加号按钮给我们当前的设置加入一个新的安置选项。然后取消勾选它。

    就像你看到的,那个view立刻消失了。由于我们在设置里面反向安置了它。我们立刻就能看到。执行app。你能看到它在纵向的portraitiphone上消失了,可是当你旋转到横向的landscape的时候又回来了。

    当然它也一直安置在ipad上面由于ipad仍然使用的是主要的设置。

    4、SizeClasses和ImageAsset及UIAppearence

    ImageAsset里也加入了对SizeClasses的支持。也就是说。我们能够对不同的SizeClass指定不同的图片了。在ImageAsset的编辑面板中选择某张图片。Inspector里如今多了一个Width和Height的组合,加入我们须要相应的SizeClass,然后把合适的图拖上去。这样在执行时SDK就将从中挑选相应的Size的图进行替换了。不仅如此,在IB中我们也能够选择相应的size来直接在编辑时查看变化。

    15.jpeg

    实际做起来实在是太简单了..但拿个demo说明一下吧,比方以下这个实现了竖直方向Compact的时候将笑脸换成哭脸--当然了,一行代码都不须要。

    16.gif

    另外,在iOS7中UIImage加入了一个renderingMode属性。我们能够使用imageWithRenderingMode:并传入一个合适的UIImageRenderingMode来指定这个image要不要以Template的方式进行渲染。在新的Xcode中,我们能够直接在ImageAsset里的RenderAs选项来指定是不是须要作为template使用。而对应的,在UIApperance中,Apple也为我们对于SizeClasses加入了对应的方法。

    使用+appearanceForTraitCollection:方法,我们就能够针对不同trait下的应用的apperance进行非常easy的设定。

    比方在上面的样例中。我们想让笑脸是绿色,而哭脸是红色的话。不要太简单。首先在ImageAsset里的渲染选项设置为TemplateImage,然后直接在AppDelegate里加上这样两行:

    17.gif

    1
    2
    UIView.appearanceForTraitCollection(UITraitCollection(verticalSizeClass:.Compact)).tintColor=UIColor.redColor()
    UIView.appearanceForTraitCollection(UITraitCollection(verticalSizeClass:.Regular)).tintColor=UIColor.greenColor()

    完毕,仅仅只是拖拖鼠标。两个代码简单的线条,那么颜色的变化可以庆幸,这真的是一个伟大的事情一大快所有的人。

    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    leetcode 114. 二叉树展开为链表
    leetcode 338. 比特位计数
    leetcode 128. 最长连续序列
    leetcode 22. 括号生成
    leetcode 200. 岛屿数量
    leetcode 70. 爬楼梯
    P6072 『MdOI R1』Path
    CF1574D The Strongest Build
    『MdOI R1』Treequery
    CF1562E Rescue Niwen!
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4628419.html
Copyright © 2011-2022 走看看