zoukankan      html  css  js  c++  java
  • 开始iOS 7中自动布局教程(一)

    你是否曾经想让你的app在横竖屏方向上看起来都表现良好而受挫?是否在做支持iPhone和iPad屏幕布局界面时几近大小便失禁?今天我将给你带来好消息!
     
    一直为大小相同的屏幕设计一个用户界面并不难,但如果屏幕的尺寸改变的话,UI元素的位置和大小也需要相应的做出改变。
     
    到目前为止,如果你的设计相当的复杂,那么你必须编写大量的代码来适应这样的布局。你应该很高兴,现在这样的情况再也不存在了--iOS6为iPhone和iPad带来了一个极好的新特性:自动布局。Xcode 5和 iOS7中对自动布局做出了改善!如果你曾经尝试着在Xcode4中使用自动布局并最终做出放弃,现在是该给Xcode5一次机会了。
     
    在你的程序中,自动布局不仅可以很容易的支持不同大小的屏幕,一个额外的功能,它也使得本地化几乎变得微不足道。你不再需要为每种你希望支持的语言创建新的nibs或storyboards,包括像Hebrew或Arabic这样从右到左的语言。
     
    这个教程将向你展示如何使用Interface Builder开始自动布局.在iOS6 教程中,我们进一步讨论过这个教程,然后有一个基于这个知识点完整的新章节,并且向你展示如何通过代码完全释放出自动布局的能量。
     
    注意:我们已经开始将iOS6教程中相应的章节更新到iOS7-这只是先给大家解解馋!当我们完成后,所有iOS6 PDF教程的订阅者将会得到免费的更新下载。
     
    so,备好零食和你喜欢的咖啡,准备成为自动布局的达人吧!
     
    springs和struts的问题
    你肯定很熟悉autosizing masks-也被认为是springs&struts模式。autosizing mask决定了当一个视图的父视图大小改变时,其自身需要做出什么改变。它有一个灵活的或固定不变的margins(struts)吗?它的宽和高要做出什么改变(springs)?
     
    举个例子,一个宽度灵活的视图,如果其父视图边框,那么它也会相应的变宽。一个视图右边拥有固定的margin,那么它的右边缘将会一直粘住其父视图的右边缘。
     
    autosizing系统在简单的情况下非常奏效,但当你布局变得更复杂时,它立马跪了。让我们看一个springs和struts不能处理的示例。
     
    打开Xcode5,创建一个基于Single View Application模板的iPhone项目。叫做"StrutsProblem":
    点击Main.storyboard。在你做别的之前,首先将这个storyboard的自动布局关了。你需要在File inspector,第一个选项的第六个tabs里:
    将Use Autolayout的box勾选去掉。现在storyboard使用旧的struts-and-springs模型。
     
    注意:任何你使用Xcode4.5或更高版本中,nib或者storyboard文件都默认激活了自动布局。因为自动布局是iOS6以及以上系统的一个新特性,如果你想使用最新的Xcode开发兼容iOS5的程序,你需要将这个选项去掉。
     
    拖拽三个新的视图到主视图上,并且像这样排列起来:
    为了表述更清楚,这里给出每个视图的颜色,这样你就能分清哪个是驴子哪个是马了。
    每个视图的inset到窗口的距离都是20点;视图之间的距离也是20点。底部的视图的宽是280点,上面两个视图的宽都是130点。所有的视图的高都是254。
     
     
    在iPhone Retina 4-inch simulator上运行这个程序,并且将模拟器旋转到landscape。程序看起来便变成这副鬼样,这不是我想象的那样:
    注意:你可以使用HardwareRotate Left和Rotate Right菜单选项旋转模拟器,或者通过按下键盘上的? 键,同时按下←或→。
     
    而你想象的程序在landscape应该像这样
    很明显,三个视图的autosizing masks留下了一些需要改进的地方。将左上方视图的autosizing设置改成这样:
    这将会让视图贴附左上边缘(不是右下边缘),并且当父视图大小改变时,重新调整自身水平和垂直方向的大小。
     
    同样的,右上方视图的autosizing设置改成这样:
    已经很接近了,但又不完全一样。视图之间的padding不正确。换个说法就是视图的大小不完全正确。问题出在当父视图改变大小时,autosizing masks告诉子视图调整大小,但又没告诉子视图该调整多少(坑儿?)。
     
    你可以调戏autosizing masks-比如,改变灵活宽度和高度设置(springs)-你不会得到完全正确的三个间距20点的视图。
    为了解决这个springs和struts方法的布局问题,非常不幸,你需要额外写一些代码。
     
    在旋转用户界面之前、之间、之后,UIKit会发送一些消息到你的视图控制器,你可以截获这些消息,从而对你UI做出改变。代表性的像viewWillLayoutSubviews,你会重写这个方法从而改变任何需要重新排列的视图的frame。
     
    在这之前,你需要先做出一个outlet属性来引用这个视图。
     
    切换到Assistant Editor模式,按住Ctrl,将三个视图都拖到ViewController.m中去:
    当视图控制器旋转到一个新的方向,这个回调将会被调用。它会监控视图控制器旋转的方向,并且适当的调整视图大小-在这种情况,根据已知iPhone屏幕大小会有一个hard-code(将可变变量用一个固定值来代替的方法叫做hard-code)偏移。这个回调会在一个动画block中发生,所以会动态的改变大小。
     
    暂时还不要运行这个程序。首先你需要按下面的样子重新保存三个视图的autosizing masks,否则autosizing mechanism将会和你在viewWillLayoutSubviews中设置的位置和大小冲突。
    这样就可以了,运行程序并且翻转到landscape。现在视图排列的非常号。翻转回到portrait,经核实,一切都良好。
     
    这样奏效了,但是你需要为这个非常简单的例子编写大量的布局代码。想象一下,为布局付出的努力是非常复杂的,特别是个别视图动态的改变大小,或者子视图的个数是不固定的。
     
    现在试着在3.5寸的模拟器上运行程序。我了个去。视图的位置和大小又错了,因为viewWillLayoutSubviews的hard-code坐标是基于4英寸大小的手机(320x568取代320x480)。你可以增加另一个if语句判断屏幕大小,并使用不同的坐标集,但是你可以看到这个方法很快变得不切实际。
     
    注意:另一个你可以采取的方法就是为portrait和landscape模式建立独立的nibs。当设备旋转时,你从另一个nib中装载视图并替换掉当前的那个。但这任然需要做很多工作,并且维护两个nibs也会增加问题。当你使用storyboards替代nibs的时候,这个方法也变得不切实际。
     
    自动布局拯救猿!
    现在你将会看到如何用自动布局实现相同的效果,从ViewController.m中移除viewWillLayoutSubviews,因为我们不再需要写任何代码。
     
    选择Main.storyboard,并在File inspector中选择开启Use Autolayout:
    运行程序,旋转到landscape。现在看起来像这样:
    让我们把自动布局付诸行动。当你点击顶部两个视图时,按住?键,这样两个视图都被选中了。从Xcode的Editor菜单中选择PinWidths Equally:
    再次选中两个相同的视图,选择EditorPinHorizontal Spacing。(尽管你执行完第一次Pin处理后,两个视图看起来还是被选中的,但其实他们只是在一个特别的布局关系显示模型里。所以你需要重新选择这两个视图)
     
    storyboard现在看起来像这样:
    橙色的"T-bar"形状代表视图间的约束。目前为止你增加了两个约束:一个等宽约束和一个位于两个视图间的水平约束。约束表达了视图之间的关系,并且他们是你使用自动布局建立布局最主要的工具。这货看起来有点吓人,但是一旦弄懂它的意思,便变得相当简单。
     
    为了继续为这个屏幕简历布局,执行下面这些步骤。每个步骤增加更多橘黄色的T-bars.
     
      o Top Space to Superview
      o Leading Space to Superview
      For the view on the right, choose:
      o Top Space to Superview
      o Trailing Space to Superview
      And for the big view at the bottom:
      o Leading Space to Superview
      o Trailing Space to Superview
      o Bottom Space to Superview
     
    现在你应该有了下面这些约束:
    注意T-bars仍然是橘黄色的。这意味这你的布局没有完成;自动布局没有足够的约束条件计算出视图的位置和大小。解决办法便是增加更多约束,直到他们变蓝。
     
    按下? 键并选中三个视图。从Editor菜单中,选择PinHeights Equally。
     
    现在选中左上角的视图和底部视图(像前面一样按住? 键),选择EditorPinVertical Spacing.
     
    Interface Builder看起来应该像这样:
    T-bars已经变蓝了。自动布局现在已经有足够的信息来计算出一个有效的布局。这看起来有点杂乱无章,这是因为等宽和等高约束条件占去了很大空间。
     
    运行程序并且...我说吧,不需要写一行代码便运行的很好了。不管你在哪个模拟器上运行;在3.5英寸和4英寸设备上,布局都运行良好。
    这非常酷,但是究竟你在这做了什么?自动布局让你表达出布局中的视图和其他每个视图的关系,而不是需要你指出视图有多大,放在哪儿。你需要放置以下这些关系(即我们所谓的约束)到布局中:
    1.左上角和右上角的视图总是有相等的宽度(也就是pin中第一个widths equally命令)。
    2.左上角和右上角的视图水平方向有20点距离(也就是pin中的horizontal spacing)。
    3.所有的视图总是有相同的高度(也就是pin中heights equally命令)。
    4.上面两个视图和下面那个视图垂直方向上有20点距离(也就是pin中的vertical spacing)。
    5.视图和屏幕边缘有20点空间(top,bottom,leading和trailing space相对于父视图的约束)。
     
    这些就足以表达出自动布局该怎么放置视图,以及当屏幕大小改变时该如何处理。

    你可以在左边Document Outline中看到你所有的约束,组名叫做Constraints(当你为storyboard激活自动布局时才会加进来)。
     
    如果你在Document Outline中点击一个约束,Interface Builder将会在视图中高亮出它:
    约束是一个真实的对象(NSLayoutConstraint),并且他们也有属性。比如,选择上面两个视图的间距约束条件(叫做"Horizontal Space(20)"),然后切换到Attributes inspector。你可以在那里通过编辑Constant字段改变边缘空间的大小。
    自动布局在描述视图上比springs和struts显得更有表现力。在这篇教程的剩余部分,你将会学到约束的一切,以及如何将他们应用到Interface Builder上来构造出不同种类的布局。
     
    自动布局如何工作
    正如你在上面测试样例中所看到的一样,自动布局最基本的工具是约束。一个约束描述了两个视图间的几何关系。比如,你可能有这样一个约束:
    "label A右边缘和button B左边缘有20点的空白空间。"
     
    自动布局会考虑到所有的约束,然后为你的视图计算出理想的位置和大小。你再也不需要亲自为你的视图设置frames了-自动布局会完全基于你为这些视图设置的约束为你做这个工作。
     
    自动布局以前,你一直需要为视图的frames设置hard-code,要么在Interface Builder中将他们放置在特定的坐标,或通过传递一个rectangle到initWithFrame:,或者设置视图的frame,bounds或者center属性。
     
    就你刚刚做的那个程序,你需要明确设置frames为:
    还需要为这些视图设置自动调整大小的masks:
    这再也不是你需要为屏幕设计所考虑的东西了。使用自动布局,你需要做这些:
    视图的大小和位置再也不重要了,只有约束要紧。当然,当你拖一个新建的button或label到画布上时,它会有一定的大小,并且你会将它拖到某一位置,但这是只一个用来告诉Interface Builder如何放置约束的设计工具。
     
     
    想你所想,如你所愿
    使用约束最大的优势就是你再也不需要把时间浪费在坐标上了。相反,你可以向自动布局描述视图如何和其他视图相关联,自动布局将会为你完成所有困难的工作。这叫做根据目的设计(designing by intent)。
     
    当你根据目的设计时,你表达的是你想要实现什么,而不需要关心它如何实现。"button的左上角坐标为(20,230)",现在你可以这么说了:button是垂直居中于它的父视图,并且相对于父视图的左边缘有一个固定的距离。
     
    使用这个描述,不管父视图多大或多小,自动布局都可以自动计算出你的button需要在哪儿出现,
     
    其他根据目的设计的示例(自动布局可以处理所有这些指令):
    "这两个text fields的大小需要一直相等。"
    "这两个button需要一直一起移动。"
    "这四个labels需要一直右对齐。"
     
    这使得你用户界面的设置更具描述性。你只需简单的定义约束,系统会为你自动计算frames。
     
    在第一部分你看到,即使为几个视图在横竖方向上正确的布局都需要做大量的工作。有了自动布局,你可以绕过这些麻烦。如果你正确的设置了约束,那么在横竖屏方向上,布局将不需要做任何改变。
     
    使用自动布局另一个重要的好处就是本地化。比如德语中的文本,出了名的比老奶奶的裹脚布还要长,适配起来是一件很麻烦的事。再次,自动布局拯救了猿,因为它能根据label需要显示的内容自动改变label的大小。
     
    现在增加德语,法语或者其他任何一种语言,都只是设置约束的事,然后翻译文本,然后。。。就没有然后了!
    获得自动布局窍门最好的方法就是使用它,所以这正是剩下教程中你会学到的东西。
     
    注意:自动布局不仅对旋转有作用;它还能轻易的缩放你UI的大小从而适应不同尺寸的屏幕。这并不是巧合,当iPhone5拥有更高屏幕的同时,这个技术也同时加到了iOS中!自动布局能轻易的拉伸你程序的用户界面,从而充满iPhone5垂直方向上多出来的空间。随着iOS7中的动态类型,自动布局变得更加重要了。用户现在可以改变全局字体大小设置--有了自动布局,这将变得非常简单。
     
     
    拥抱约束(courting constraints)
    关闭你当前的项目并用Single View Application模板创建一个新的iPhone项目。叫做"Constraints"。任何用Xcode5创建出来的项目都会自动假定你会使用自动布局,所以你并不需要额外做任何事情。
     
    点击Main.storyboard打开Interface Builder。拖一个新的Button到画布上。注意当你拖拽的时候,蓝色虚线将会出现。这写线用来做向导。
    在屏幕边缘以及中心的时候,都会有向导线:
    如果之前你已经使用过Interface Builder,那么你肯定看到过这些向导线。这对我们对齐控件有很大的帮助。
     
    在Xcode4中激活自动布局时,向导线有另外一个目的。你任然可以用他们来对齐,但是他们也会告诉你新的约束将会在哪儿。如果你将button沿着向导线反方向拖拽到左上角时,Xcode4中的storyboard看起来便像这样:
    有两个蓝色的东西附属在button上面。这些T-bar形状的对象便是约束了。Xcode 4的Interface Builder中不管你将UI控制器放在哪儿,它总是会给出有效的约束。理论上这听起来是个好主意,但是实践起来,在Interface Builder中使用自动布局却非常困难。
     
    幸运的是,Xcode5中已经有所好转。将button拖拽到画布上之后并看不到T-bars形状的东西:
    同时在Document Outline面板中也没用Constraints部分。得到结论:此时button上并没有设置任何约束。
     
    那这是如何运作的呢?我们之前了解的自动布局总是需要足够多的约束才能决定视图的大小和位置,但是现在我们这儿跟本没有约束。确定这是一个完整的布局?
     
    这这是Xcode5相对Xcode4来说最大的一个提升:再也不强制你总是有一个有效的布局。
     
    注意:1.运行一个无效布局的程序是不明智的,因为自动布局不能正确的计算需要将视图放在哪儿。要么视图的位置是不可预知的(约束不够),要么程序将会崩溃(约束过多)。
     
    2.Xcode4设法保证总是有足够多正确的约束来创建一个有效的布局。不幸的是,它经常会将你的约束替换为你并不想要的。这会令人很沮丧,正是因为这个原因很多开发者放弃了自动布局。
     
    3.Xcode5中,当你编辑Storyboard时它允许你有不完整的布局,但它也会指出哪些地方你还需要修改。使用Interface Builder创建的自动布局驱动用户界面变得更有趣了,使用Xcode5也消耗更少的时间
     
    如果你根本不提供任何约束,Xcode自动分配一套默认的约束,正是我们所知的自动约束。它会在程序built的编译时间中去完成这些事,而不是设计时间。当你设计你的用户界面时,Xcode5中的自动布局为了不参与你的设计方法而努力工作,这这是我们喜欢它的原因。
     
    自动约束为你的视图提供一个固定尺寸和位置。换句话说,视图总是拥有跟你在storyboard中看到的一样的坐标。这是非常方便的,因为这就意味着你可以大量的忽视自动布局。你可以为那些拥有充分约束的控件不增加约束,只为那些需要特殊规则的视图创建约束。
     
    OK,让我们玩一玩约束并看看他们能做什么。现在,按钮是在左上角,并且没有约束。确认按钮跟两个拐角向导线对齐。
     
    使用EditorPin菜单为按钮增加两个新的约束,看起来像这样:
    目前有两个约束,一个是button和main view左边缘的Horizontal Space约束,一个是button和main view上边缘的Vertical Space约束。这个关系通过约束描述起来便是:"button总是位于其父视图左上角20点处。"
     
    注意:这些其实都不是非常有用的约束,因为他们有相同的自动约束。如果你总是想你的button相对于父视图左上角,那么你还不如不提供任何约束,让Xcode为你做这些。
     
    现在拖动button并将它放到屏幕的右上角,再次和蓝色向导线对齐:
    当谈到自动布局,橙色代表坏的。Interface Builder绘制两个橙色方块:一个是虚线边框,一个是实线边框。虚线方块是根据自动布局显示视图的frame。实线方块是根据你在屏幕上放置的视图的frame。这两个应该吻合的,但是这里并没有。
     
    如何修改取决于你想要达到什么目的:
    1.你想让button附属于屏幕左边缘254点处吗?在这种情况下你需要将现存的Horizontal Space约束变大234点。这正是橙色badge中"+234"的意思。
    2.你想让button附属于屏幕的右边缘?那么你需要移除现有的约束并重新创建一个新的。
     
    删除Horizontal Space约束。首先在画布或Document Outline中选中,然后按键盘上的Delete键。
    Note:你可能会奇怪,为什么Xcode不为X轴方向自动增加一个约束。Xcode中的规则是:Xcode只为那些你没有设置任何约束的对象创建自动约束。一旦你增加一个约束,你便是告诉Xcode你接管了这个视图。Xcode将不再增加任何自动约束,并希望你为这个视图增加需要的约束。
     
    选中button,并选择EditorPin|Trailing Space to Superview.这迫使在button右边缘和屏幕右边缘增加一个新的约束。关系表达如下:"button总是位于距离其父视图右上角20点处。"
     
    运行程序并旋转到landscape。注意button如何与屏幕右边缘保持相同距离:
    当你放置一个对立于向导线的button(或者任何其他视图)并新建一个约束时,你会得到一个根据"HIG"(Apple's iOS Human Interface Guidelines document)定义的标准大小的间隔约束。对于边框来说,标准大小空间是20点。
     
    现在将button向左拖拽一点:
    这就是你表达的意思---这个button始终应该位于底部中心。注意,你根本不需要告诉Interface Builder按钮的坐标是什么,除非你想将它固定在视图上。
     
     
    通过自动布局,你再也不需要担心视图位置的精确坐标或视图大小了。相反,自动布局会根据你设置的约束得到这两个参数。
     
    你可以在button的Size inspector中看到这个经典转移,现在有了很大的不同:
    如果不使用自动布局,输入值到X,Y,Width或Height字段将会改变选中视图的位置和大小。使用自动布局后,你仍然可以输入新值到这些字段,但是如果你已经为视图设置了约束,那这可能造成视图错位。你将不得不更新约束来匹配新值。
     
    举个例子,把button的宽度改为100,画布会变成这样:
    Xcode4用Horizontal Space取代Center X Alignment约束,并且button上会产生一个新约束强制它的宽度为100 points。然而,Xcode5说,"如果你想让button宽度变为100 points,对我来说无所谓,但是你要知道约束并不是这么说的。"
     
    在这种情况下你希望button是100点宽。对此有一个特殊的约束类型:Fixed Width约束。首先按一下Undo,这样button又居中了,T-bars也变蓝了。选中button并选择EditorPinWidth。这会在button下面放置一个新T-bar:
    与其他约束不同,在button和它的父视图之间,Width约束只会应用到button本身。你可以将这个认为是一个button本身和本身之间的约束。
     
    你可能怀疑为什么button之前没有Width约束。自动布局是为何知道button有多宽?
     
    事情是这样的:button自己是知道自己有多宽。它根据自己的title text加上一些padding就行了。如果你为button设置一个背景图片,它也会考虑进去。
     
    这正是我们熟悉的intrinsic content size。并不是所有的控制器都有这个,但大部分是(UILabel是一个例子)。如果一个视图可以计算自己理想的大小,那么你就不需要为它特别指定Width或Height约束了,你将会在稍后看到更多相关内容。
    为了恢复button到最佳大小,首先我们需要移除Width约束。然后选中button,并从Editor菜单中选择Size to Fit Content。这样就能够恢复button的固有的内容尺寸了。
     
    孤掌难鸣
    向导线不但出现在一个视图和它的父视图之间,而且也会出现在相同层级的视图之间。拖拽一个新的button到画布上进行演示。如果你将这个button拖近其他对象,这时他们的向导线将会开始相互影响。
     
    将新button放到之前一个button的后面完好入位:
    它是橙色的,这意味着这个button至少还需要另一个约束。button的大小是知道的(使用intrinsic content size),并且还有一个button在X轴上的约束。只剩下Y轴没有约束了。
     
    这种缺失约束的情况是很容易确定的,但是更复杂的设计可能就没这么明显了。幸运的是,你不再需要敏思苦想,Xcode已经记录并可以确切的告诉你缺少了什么。
     
    在Document Outline中会有一个红色的小箭头,就在View Controller Scene后面。点击这个箭头便会看到所有Auto Layout问题:
    这次弹出菜单有不同的选项了。这次菜单的选项是基于上下文环境的—你在哪些视图间拖拽以及鼠标移动的方向。选择Bottom Space to Bottom Layout。
     
    现在新button有一个位于屏幕底部的Vertical Space,也有一个跟其他button相关联的Horizontal Space。虽然空间非常小(只有8 points),T-bar可能不大容易看到,但它就在那里。
     
    点击Document Outline里面的Horizontal Space(8):
    为新button设置一个绿色背景色,这样就可以更容易看出它的范围。
     
    因为你将两个button对齐在一起,现在他们之间存在HIG推荐的8 points间隔。按住Ctrl在两个button之间拖拽将这变为一个约束。从弹出菜单中选中Vertical Spacing。
     
    Note:“HIG”是“iOS Human Interface Guidelines”的简称,包含Apple推荐的良好的用户界面设计。任何iOS开发者都有必要读一读这个规范。HIG解释了哪些UI元素适合在什么情况下使用,以及最佳使用方式。你可以在这里找到。(https://developer.apple.com/library/ios/DOCUMENTATION/UserExperience/Conceptual/MobileHIG/Introduction/Introduction.html
     
    然而你并没有被限制在controls间的标准间隔。约束是成熟的对象,就像视图一样,因此你可以改变它们的属性。
     
    选中两个button之间的Vertical Space约束。你可以在画布上点击T-bar,虽然这有点麻烦。目前最简单的办法就是在Document Outline里面选择约束。一旦你选中约束,再切换到Attributes inspector:
    button必然会保持他们垂直方向的排列,但是水平方向就不了!原因很明显:绿色button还没有X轴方向的约束。
     
    为绿色button增加一个到屏幕左边缘的Horizontal Space并不能解决问题。这样的约束只会让绿色按钮总是保持在同一个X轴坐标,即便是在横屏模式下。这看起来感觉不大对,所以你需要表述这样一个目的:
    “黄色button会一直水平居中,蓝色button左边缘会一直跟黄色button左边缘对齐。”
     
    你已经为第一种情况创建了一个约束,但是第二个并没有。Interface Builder为对齐显示了向导线,这样你就可以将上面button一直拖拽到跟黄色左边缘对齐的位置:
     
  • 相关阅读:
    原码、反码、补码,计算机中负数的表示
    [转]Vue 2.0——渐进式前端解决方案
    关于MySQL的tinyint(3)问题
    js对象的深拷贝及其的几种方法
    深入 js 深拷贝对象
    JS 数组克隆方法总结
    Undefined class constant 'MYSQL_ATTR_INIT_COMMAND'
    邮件措辞小计
    Forbidden You don't have permission to access / on this server PHP
    正则表达式
  • 原文地址:https://www.cnblogs.com/ChouDanDan/p/5131059.html
Copyright © 2011-2022 走看看