autoResizing autoLayout和sizeClass

autoResizing autoLayout和sizeClass


1. autoResizing

autoresizing是苹果早期的ui布局适配的解决办法,iOS6之前完全可以胜任了,因为苹果手机只有3.5寸的屏幕,在加上手机app很少支持横屏,所以iOS开发者基本不用怎么适配布局,所有的ui控件只要相对父控件布局就可以了,没错autoResizing就是一个相对于父控件的布局解决方法;注意:它只能相对父控件布局


在xcode中可以通过可视化的界面调整也可以通过代码去控制

Snip20141209_1.png

在用autoResizing的时候需要关闭autoLayout和sizeclass(如果是用xcode6)
他们之间是互相冲突的

可以通过图片看到autoResizing通过可视化能调整的只有6根线刚好和它的6个枚举值对应

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
    UIViewAutoresizingNone                 = 0, 
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

外边的4根线用来设置当前view距离父控件的上、下、左、右的距离是否固定;
内部的两根线来设置view是否跟随父控件来自适应width和height;
代码则可以通过view.autoresizingMask = ...来设定autoResizing值;
autoResizing的功能仅此而已;显然不够用;


举例:
1:让两个等宽等高的view之间的间距永远固定,如图的红色view和蓝色view,想让它们之间的距离固定通过autoResizing是不行的;因为autoResizing是相对父控件进行布局的,不可以在两个兄弟view之间建立布局关系;

Snip20141209_3.png
Snip20141209_4.png

可以看到一个横屏和竖屏就已经不能满足需求了,更别说屏幕尺寸还要变大
当然你有可能通过其他复杂的辅助手段实现,但是很麻烦;因为不仅仅是横竖屏幕;屏幕尺寸也要变了;
autoResizing就到此为止,显然它已近过时,了解一下就可以了;


2. autoLayout iOS6之后

做苹果开发的一个好处是有一个很好的东家,苹果公司,他不仅很注重用户体验,而且还不忘为开发者去除一些不必要的麻烦(例如:ARC的出现...)
autoLayout:可以在任意两个控件之间建立布局关系,可以是父子view也可以是兄弟view;功能强大了许多,当然学习成本也高了不少;
如图:

Snip20141209_5.png

为了方便了解先勾选autoLayout就可以了,sizeclass先别勾了(如果是xcode6)
autoLayout的设置功能就上图中下方的红色方框中;


左起第一个:

Snip20141209_7.png

通过图中红色框圈住的地方可以看出来,该处功能是设置多个view之间的对齐方式,所以设置的时候要同时选中多个View进行设置

**第二个: **

Snip20141209_10.png

这一部分相当于是一个autoResizing,强大之处在于可以是任意两个view的相对布局,可以设置距离父控件的上下左右位置(红色框),还有自身的宽高,还可以相对其他控件设置宽高(蓝色框)

第三个:

Snip20141209_11.png

这部分是用来添加、删除、和更新约束的,上半部分是对于选中view的约束更新,下边是容器中得所有view的约束

除了上边,还可以通过control按键配合拖线来做autoLayout,如图:

autoLayout脱线.gif

===
例子:用autolayout完成刚才autoResizing不能完成的任务,这里继续加大难度,除了让红色view和蓝色view等宽等高,而且距离始终保持不变以外,还要让两个view整体垂直居中于屏幕;先看最终的效果图

Snip20141209_17.png

横屏效果

Snip20141209_19.png

可以看到不管是横屏竖屏还是大屏小屏,都是没问题的,到中分线的距离固定且相等;


下边开始一步一步通过autoLayout完成

第一步:

先让两个view等高等宽, 在xcode中同时选中红色view和蓝色view,勾选等宽,等高;

Snip20141209_12.png

这里也可以用另外一种方式:按住control进行拖线,为了可读性和看着直观就不用了;
这时请注意看xcode得左上角,会有红色的箭头出现,表示添加的约束不完整,先说明:约束的完整性,一个view在视图中的位置相对固定(宽高和相对位置),那么约束就完整,注意是相对固定; 而这里我们只是让两个view等宽和等高,位置在哪里并没有说明,约束当然不完整;所以有红色的错误;

Snip20141209_14.png

知道红色的报错箭头正常之后,我们继续,添加的每一条约束都是一个实体,可以在view中看到,点击每条约束可以在xcode的右侧编辑栏中看到约束的线性公式,这个线性公式正是苹果工程师的智慧结晶,巧妙的运用数学的智慧值得我们去学习和体会;

Snip20141209_15.png

顺着这几个文本框从上往下读可以得到如下的线性公式(Priority优先级不算入公式):
view.width Equal view.width * 1 + 0
化解得:
view.width = view.width 就这样实现了两个view之间的width关系的描述,将这个公式转化成代码应该很easy吧.. 哈哈

说明:这里我们查看的是width约束,所以是view.width,两个view可以分别认为是红色view和蓝色view,可以相互对调位置
也许上边不伦不类的公式很难理解,那么假设红色view的宽度为x, 蓝色view的宽度为y,对于任意两个实数x,y,都可以用下面的线性公式(或者说是一次线性函数)表示他们之间的关系:
y = k·x + b 其中k是系数(就是上图中的Multipliter), b是常数(是上图中的Constant);
就是这个小学已经学习过的一次函数,可以满足任何两个view的任何边距关系;


第二步:
了解这些之后,回到正题,刚才只是添加了两个view等宽等高,接下来,为了保证不会错乱,我们可以一个一个的来完成约束,这里先固定红色的view:
这里采用control拖线的方式:

redCenter.gif

这里我们让红色view和蓝色的centerY到父view的centerY都是70,这样操作后相当于两个view在竖直方向上的位置固定;水平方向和每个view的快高具体是多少都还没固定;所以xcode左上角继续报红色错误
第三步:
固定水平位置和宽高,这里固定宽高,只需要固定其中的一个即可,因为两个view的宽高相同;

horiAndWG.gif

可以看到添加完后红色的箭头变成了黄色,这表示,约束完整只差一步update了,这是你可以通过点击黄色箭头update或者如图:

update.gif

好了,到此就搞定了;这个看似只有两个view,但是这个需求已经是相对复杂了;

案例二:
通过上边的案例一可以看出autoLayout可以设置任意两个view之间的约束,可以说能实现任何想要布局;
下边的案例:让四个view始终等分屏幕:
效果图(为了说明效果,中间留了一个像素的间距):

等分屏幕.gif

思路:先固定好其中的一个,然后其他view和这个view等宽等高,然后设置相对父控件的约束;

代码实现autolayout

每一条约束都是一个:NSLayoutConstraint对象

NSLayoutConstraint *layout = [NSLayoutConstraint constraintWithItem:(id) 
attribute:(NSLayoutAttribute) relatedBy:NSLayoutRelationEqual toItem:(id) 
attribute:(NSLayoutAttribute) multiplier:(CGFloat) constant:(CGFloat)];
[self.view addConstraint:layoutConstraint]

该方法正是刚才线性公式 y = k·x + b 的代码表示;所以虽然很长,但是很容易读懂;最后要记得把约束添加到view上,添加的时候必须要注意如果这个约束是父子view关系,约束必须加在父view上边;
但是用代码去实现autolayout实在是麻烦,刚才的两个例子,每个例子中得约束有15个以上,如果用代码实现,要创建15个以上的NSLayoutConstraint对象,还有弄清楚关系,不能写错;苹果又为开发者考虑到了这一点;于是推出了为了写autolayout方便的VFL语言;

VFL

严格来讲不算语言,一中语法类似正则表达式的专门用来写autolayout的;但是vfl并没有带来简化,其繁琐程度基本上是写一行就不想写第二行的程度:

NSString *vfl1 = @"|-hPadding-[_headerL]-hPadding-|";   
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl1
options:0 metrics:metrics views:dict1]];   

和正则用法差不多,先用字符串写一些匹配规则,然后就调用后边的方法去解析这个规则到view上;我个人一直认为编程语言应该从简,将复杂的东西屏蔽掉,使我们开发起来更高效,也有更多时间去处理好业务; 毕竟我们每天做的情就是让用户简单起来,把复杂留给自己;所以vfl不用也罢;

一个label用autoLayout约束,会自动计算好宽高,并且上边和下边不会留空白;

Snip20141210_1.png

3. 苹果界面设计的统一:sizeClass

<p>与其说科技的发展,拉近了空间中任意两点的距离,让交流、信息传递更加便捷;倒不如说由于交流和信息传递的需求更加迫切而推动了科技的进步;大屏显然是一典型的例子,屏幕尺寸的相对增大,一定程度上方便了交流和信息传递,反之,相对小的屏幕对信息传递会有一定的局限;所以苹果推出大屏幕的手机也是人类进步的需要,并不是什么跟风,扯淡结束;
屏幕大了,尺寸多了,带给开发者的自然是适配方面的工作量和思考;正如大家知道的那样;苹果是一家最具追求的公司,他当然会推出可行的解决方案就是sizeClass;
sizeClass: 对屏幕尺寸进行了抽象:不在拘泥于具体尺寸;因为尺寸一直都在变化,我们如果按照尺寸去做适配,一定会很累的;</p>

sizeClass针对iOS设备的屏幕进行了抽象分类:

  1. compact (紧凑-小)
  2. Any (任意)
  3. Regular (宽松-大)

总结几点:

  • sizeClass只是对屏幕进行了抽象分类;具体做屏幕的适配还得用autoLayout;
  • 没有了横竖屏的概念,也没有了具体尺寸,不用在去谈具体的iphone5还是ipad air;
  • 把高度和宽度都抽象为上边的3种,3*3也就是总共9种类型;是9种类型,不是9种屏幕尺寸;

这样做的结果就是你可以做好一个interface builder适配,然后不管在iphone还是ipad中都可以用了;
这就是苹果的意愿;打开xcode如果新建一个universal项目,在xcode6之前会默认有两个storyboard,一个是iphone的,一个是ipad版本的;xcode6之后只有一个,并且是正方形的,也就是说不管你做那种屏幕尺寸的app(无论是ipad还是iphone),都只用这一个storyboard就可以了;

了解3种抽象:

xcode中得样子:

Snip20141210_2.png

具体来看:

20140915091835218.png

图中9个格子代表 3*3的9中抽象;具体每种代表了那些含义可以选中看看;
比如iphone的竖屏它是这样抽象的:compact width * regular height


通过sizeclass对屏幕进行分类,然后用autolayout去适配布局,可以说能实现任何想要的效果,并且不用区分设备而做不同的IB了,一个IB全部搞定,不管是iphone还是ipad;
案例:

有个view 100 * 100,在iphone竖屏时居于左上角,横屏时在右下角,ipad中在正中间;

像这种过去看来变态的要求,在现在来说小菜一碟;

  • 步骤一:处理竖屏情况
    先用sizeclass固定屏幕为iphone竖屏:compact Width | Regular Height
Snip20141210_3.png

然后添加view到ib上,用autolayout固定尺寸100 * 100;并且在左上角;这里让它居左20,居上20;

Snip20141210_5.png

然后update frame即可
后边的同理解决,只不过要选对正确的sizeclass


iOS8加了sizeclass后,控件也多了个属性,在storyboard上托个label出来(以label为例),选中,在右边的菜单区域可以看到:installed,这个是用来控制改控件什么情况下显示,当前什么都没约束,表示Any * Any,就是不管是iphone什么尺寸还是ipad什么尺寸都可以显示,点击左边的小加号+可以用sizeclass控制什么情况显示;同样的还有字体、图片显示;

Snip20141210_7.png

在不同的屏幕下显示不同的字体:

Snip20141210_9.png

... 类似的功能还有很多
这个功能的思想和UIButton的三种状态(normal, highlighted, selected)类似,只不过这里有9种状态;


xcode6预览

当屏幕种类变的越来越丰富的时候,如果要查看不同屏幕之间的适配情况,在过去是要不停的切换模拟器,然后运行看效果的,而切换模拟器是很耗时的一件事;xcode6之后,我们可以不用运行直接查看适配的情况;
对于刚才的事例进行如图操作即可不运行查看适配效果;

预览功能.gif

代码预览 @IBDesignable和@IBInspectable

这两个属性是xcode6新出的特性
如果不是通过IB创建view,而是通过代码创建的View,如何在不运行程序的情况下实时的渲染在IB上呢,就是通过@IBDesignable和@IBInspectable;@IBDesignable告诉Interface Builder这个类可以实时渲染到界面中,但是这个类必须是UIView或者NSView的子类。通过@IBInspectable可以定义动态属性,即可在attribute inspector面板中可视化修改属性值。

示例(swift编写): 这个例子自定义一个UIView的子类,该子类拥有一个UIButton

@IBDesignable
class MyCustomView: UIView {
  @IBInspectable var buttonTitleColor: UIColor! //  button title color
  @IBInspectable var buttonTitle: String!       //  button title
  @IBInspectable var buttonFrame: CGRect!       //  button frame
  var myButton: UIButton!
  
  override init(frame: CGRect) {
    // init stored properties
    buttonTitleColor = UIColor.redColor()
    buttonTitle = "我是按钮"
    buttonFrame = CGRectMake(0, 0, 100, 50)
    
    myButton = UIButton(frame: buttonFrame)
    myButton.setTitleColor(buttonTitleColor, forState: .Normal)
    myButton.setTitle(buttonTitle, forState: .Normal)
    
    // call super initializer
    super.init(frame: frame)
    
    // add button to self
    addSubview(myButton)

  }
  required init(coder aDecoder: NSCoder) {
    // init stored properties
    buttonTitleColor = UIColor.redColor()
    buttonTitle = "button title"
    buttonFrame = CGRectMake(0, 0, 100, 50)
    
    myButton = UIButton(frame: buttonFrame)
    myButton.setTitleColor(buttonTitleColor, forState: .Normal)
    myButton.setTitle(buttonTitle, forState: .Normal)
    
    // call super initializer
    super.init(coder: aDecoder)
    
    // add button to self
    addSubview(myButton)
  }
  
  override func layoutSubviews() {
    // refresh button state through attribute inspector
    myButton.setTitleColor(buttonTitleColor, forState: .Normal)
    myButton.setTitle(buttonTitle, forState: .Normal)
  }
}

该类在界面上加了一个Button,并且添加了三个属性,颜色、title、frame;这三个属性都是用来描述button的,并且在前边都加上@IBInspectable表示能够在IB中实时预览,不用运行程序启动模拟器即可;
同时还重写drawRect方法画了一个矩形;
这个时候打开storyboard,添加一个View(为了控制gif图的大小,图中的View事先加好了约束,并且背景色改为蓝色,方便识别);修改class为MyCustomView;预览就会出现;同时我们将开始画得矩形改为椭圆,然后再次查看storyBoard,预览已经变为椭圆,真是太爽了,不用在浪费时间等待模拟器启动去观察UI的布局了;

inspectable预览.gif

各种苹果设备的尺寸和分别率

一图胜千言

51530583gw1ek7mqv36zxj20go099jrm.jpg

QQ20141130-1@2x.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341

推荐阅读更多精彩内容