您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
iOS开发~UI布局
 
作者:jgCho 来自于:博客园 发布于 2016-2-25
  2200  次浏览      19
 

iOS开发~UI布局(一)初探Size Class

随着iOS8系统的发布,一个全新的页面UI布局概念出现,这个新特性将颠覆包括iOS7及之前版本的UI布局方式,这个新特性就是Size Class。Size Class配合Auto Layout可以解决所有(包括iPhone及iPad)iOS设备屏幕尺寸及屏幕旋转时候的UI适配问题。

二、为什么要使用Size Class

直到iPhone6发布后,目前iOS设备的屏幕尺寸已经有4种了,如图:

iPhone6没出现之前,还可以通过代码来适配两种尺寸的UI,但iPhone6发布后,渐渐的发现以前的方式可能真的要淘汰了,因为以后可能还要面对更多的屏幕尺寸。显然硬编码UI的Frame的时代要过去了,要使用Auto Layout了。

自从iOS6开始就引入了Auto Layout,但一直没用推广使用,原因有很多(例如:Auto Layout本身不是很成熟、硬编码可以解决iPhone仅有两种屏幕尺寸的UI适配、iPhone项目和iPad项目分开来进行等)。

Size Class是配合Auto Layout来使用的,让Auto Layout方式变得不那么复杂。

三、该如何使用Size Class

要使用Size Class,首先要安装最新的Xcode6,新建一个Single View Application template项目,选择Main.storyboard-> View,查看Inspector属性:

默认Size Classes就已经使用了,当然如果不想使用它也可以关掉,然后使用旧的布局方式。但如果选择使用Size Class,然后关掉Auto Layout,Xcode会弹出一个警告框:(稍后就会明白为什么!)

选择cancel,因为接下来要使用Size Classes。

Size Classes其实就是将iOS设备屏幕的Size进行分类,如何分类的呢?

对于iOS设备(无论iPhone还是iPad),宽度和高度都各分为3种情况:紧凑(Compact)、规则(Regular)、任何(Any)

其中“任何”(Any)包含紧凑(Compact)、规则(Regular)类型。如果屏幕宽度用w表示,高度用h表示,那么w(Regular)/h(Regular)组合就是iPad屏幕尺寸(size)的类别(class),无论iPad横屏还是竖屏,屏幕尺寸类别都是w(Regular)/h(Regular),上图可以很清楚的表达。

官网也列举了一些:

到这里应该明白了,Size Classes是将屏幕尺寸的种类做了进一步的抽象。那有一个问题,分类的有什么用?该怎么用啊?

以前为不同的iOS设备尺寸或者同尺寸横竖屏不同适配UI,都要根据实际情况而去计算frame。使用Size Classes是根据当前的屏幕size类型而使用Auto Layout方式进行布局了,要摒弃之前计算frame的思路,而改用相对布局的思维去思考(实际上还是要计算frame)。

而且Xcode6最大的突破也是这里,不在需要建立不同尺寸的storyboard了,只要建立一个,然后修改其view的size就可以做各种屏幕尺寸的适配,如下:

例如我要做iPad页面设计,就设置成w (Regular)/h(Regular)

然后同样的工程,又要兼容横屏的iPhone6 plus,就可以把view的size class设置成:w (Regular)/h(Compact),然后继续适配

然后当程序跑在不同的设备上,或者设备横屏和竖屏切换,就能显示相应的UI了。

说了这么多,还是动手实验一下吧:

适配iPhone6,在RootViewController的view上添加一个新的view,让这个新的view无论屏幕横屏还是竖屏时候都距离其superview的边缘50点宽,并且横屏时候为绿颜色,竖屏时候为红颜色。

1、新建项目(刚刚已经新建了一个AL8的项目,不重复步骤了)

2、切换size class为wCompact/hRegular模式

并且添加一个view,不用管其frame,并设置其背景色为红色

接下来选中红色的view,然后点击Xcode顶部工具栏的Editor-Pin,然后分别添加红色view相对superview边框的约束(上下左右)

添加约束的过程中会看到约束的线是黄颜色,表明当前的约束还不能确定view的frame,需要继续添加,当添加完4个约束后,约束线的颜色是蓝色的,表明当前约束是正确的。

然后选中约束,设定约束的值(我们不是想让新的view距离其superview边界50点宽吗!),4个约束都要设置。

设置完后点击下view会自动更新frame,应该是这样的:

3、切换size class为wRegular/hCompact模式,然后重复第二步中的设置,区别是新添加的view背景颜色设置为绿色。

4、大功告成,用模拟器运行下吧,模拟器要选择iPhone6 plus哦!然后旋转屏幕看看是不是我们想要的效果!

思考:把模拟器切换为iPhone6、iPhone5、iPhone4s、iPad会有什么现象!下一篇会解释!

iOS开发~UI布局(二)storyboard中autolayout和size class的使用详解

一、概要:前一篇初步的描述了size class的概念,那么实际中如何使用呢,下面两个问题是我们一定会遇到的:

1、Xcode6中增加了size class,在storyboard中如何使用?

2、auto layout该如何与size class配合来进行UI布局?

二、了解一件新事物的最好的办法就是实践,让我们揭开那神秘的面纱:

例子1、新建一个Single View Application template项目Demo1,拖拽一个newView到rootView上,并设置背景色为绿色, 然后不做任何其他修改,运行项目,模拟器选择iPhone6,运行效果如图:

横屏:

竖屏:

把模拟器更改为iPad、iPhone4s等,然后再旋转屏幕,发现绿色的newView的位置几乎没有变化。查看Document Outline发现没有任何constraint(约束)作用于绿色view上,理论上如果一个view没有任何constraint,那么view将没有位置及大小,但目前来看是没问题的,何解?

查看view的size inspector,发现秘密了:

原来如果不给一个view添加任何constraint,系统会自动给view添加左、上、宽、高四个约束,就是说新绿色的view会有固定的宽和高,然后以屏幕左上角为参考点,有一个系统添加的默认位置。(如果我们向绿色view添加任何其他约束,那么系统自动添加的约束将实效)

还有一个问题是,刚刚切换了很多不同类型的模拟器,相当于切换了不同的size class,但显示绿色view都正常,而size class的存在的目的就是为了区分不同的size class(例如:iPhone4s横屏wCompact/hCompact,iPhone4s竖屏wCompact/hRegular,iPad横竖屏都是wRegular/hRegular),然后来做不同的UI布局,何解?

查看当前的size class:

当前的size class为wAny/hAny,也就是说在size class为wAny/hAny的时候添加constraint,在其他size class的时候也生效。其实从字面上也可以看出,Any就是任何的意思,Compact和Regular是Any的子类。

例子2、基于iPhone适配界面,添加三个view到rootView上,然后无论横屏还是竖屏,新添加的三个view之间及与屏幕边框的距离都保持不变的间距20点宽,效果如图:

新建一个Single View Application template项目Demo2,因为要适配iPhone横竖屏,所以修改size class为wCompact/hRegular来适配竖屏:

拖拽3个view到rootView上,并设置其背景颜色

为了满足设计要求,要添加如下constraint:

(1)设定绿色view距离superview左边距和上边距;

(2)设定黄色view距离superview右边距和上边距,相对绿色view的的左边距;

(3)设定蓝色view的左边距和右边距和下边距,上边距离绿色view下边的距离;

(4)设定绿色view与黄色view等宽

(5)设定蓝色view与绿色view等高

现在开始动手吧:选中绿色view,Eidtor->Pin->Leading Space to Superview给绿色view添加相对其superview的左边距,然后选中constraint,修改约束的值为20,其他constraint以此类推:

添加完如图:

其中红色框部分清晰的表达了所添加的constraint;

蓝色框部分时添加的constraint,目前为黄色线,表明当前的constraint还不能定位view,当一个view的constraint正确的时候,constraint的颜色会变为蓝色。

绿色线框的部分表达了constraint的数值,我们想让边距为20,所以设置数值为20 。wC hR Installed表明当前constraint适用于wC hR这种size class,不适合any any的size class。

添加绿色view与黄色view之前的距离时候,由于是设定两个子view的constraint,所以要选中两个view,然后Editor->Pin ->Horizontal,设定值为20:

同样方法Editor->Pin ->Width Equally,设定绿色view与黄色view等宽度,蓝色view与绿色view等高,结果如图:

但发现constraint颜色仍然后黄色,原因是当前view的位置和constraint希望的不一致,更新下frame(要选中3个view,因为constraint关联3个view)或者点击Document Outline中的黄色小箭头,然后会看到具体的constraint信息来一步步调试,这个也是Xcode6最有突破的地方:

然后效果如图:

然后运行下项目吧,发现确实和预期的一样。然后旋转屏幕,是不是发现横屏时候白了,屏幕什么都没有了?原因是我们仅仅适配的竖屏,横屏还没有适配啊!

修改size class,iPhone4s横屏的size class为wCompact/hCompact,而iPhone6 plus为wReguage/hCompact,那我们不如设置为wAny/hCompact吧!然后安装上边适配竖屏的方式适配横屏。适配好后再次运行,横竖屏都应该是我们想要的了。

例子3、新建一个Single View Application template项目Demo3,添加一个view,是这个view的宽度和高度都是100点宽,并且始终居中于superview,效果如图:

这个例子很简单,也很容易实现,目的是补充以上两个例子没有提及到的一些细节。

在rootView上添加一个view,设定背景色为绿色:

由于不打算区分是哪种iOS设备,所以size class选择wAny/hAny,然后绿色的view随便放到superview上就可以,Auto Layout的理念就是不用去管具体view的frame,要注意的是这个view最终想如何的显示,属于基于目的设计的范畴。

这个例子要完成两件事情:

(1)设定约束使view的宽和高为100点宽

(2)设定约束使view始终居中于superview

先做第(1)件事:选中view,然后Editor->Pin->Width,设定为100,同样方法设定Height

然后第(2)件是让view居中:还是选中view,然后Editor ->Aligh ->Horizontal Center in Container,同样方法设定垂直居中对齐:

然后会发现Document Outline右上角有一个黄色小箭头:

点击黄色箭头进入structure页面,可以看到一些提示信息,可以了解当前布局存在问题,指导下一步该做什么。在view中看到一个黄色虚线框,这个框代表目前约束得到的view,在structure页面有一个黄色的点,点击后会提示你如何修改:

选择Update Frame就是按照当前的约束去更新view,选择Update Constains是按照拖拽进去view的frame更新约束。在这个例子中Update Frame是我们需要的。然后运行项目看看是不是我们想要的效果吧!

一、概要

通过对iOS8界面布局的学习和总结,发现autolayout才是主角,autolayout是iOS6引入的新特性,当时还粗浅的学习了下,可是没有真正应用到项目中。随着iOS设备尺寸逐渐碎片化,纯粹的hard code方式UI布局将会走向死角,而autoresizing方式也有其局限性,所以无论如何autolayout都将成为UI布局的重要方式。

前两篇以发烧友心态对iOS8界面布局的主要元素size class和autolayout进行了探索,发现要完全掌握autolayout需要大量的时间去实践总结。所以深入思考autolayout是很有必要的。你可能有和我同样的疑问,如下:

1、以后一律使用autolayout吗?除了在storyboard中使用autolayout,代码方式autolayout如何使用?

2、好像忽略了一个重要问题,就是view动画在autolayout如何实现?

3、autolayout有没有局限性和解决不了的问题?兼容性怎么样?效率怎么样?

4、……

二、研究开始

1、直接说以后都应该使用storyboard+autolayout感觉是不负责的说法,读了好多网络的帖子,最后总结如下情况使用autolayout会有帮助:

a 当需要展示的内容很多并且尺寸不固定;

b 程序需支持屏幕旋转(主要是iPad程序,iPhone程序横屏的场景有点非主流);

c 程序通用于iPhone和iPad;

但storyboard中使用autolayout有利有弊,好处当然是可视化,实现简单功能很节省时间,但也有弊端,例如不小心移动一个控件就会让弄乱那些约束。抛开storyboard而使用autolayout,就需要代码定义约束了,而且代码量也不是很大。当app中一些view的出现时根据网络数据来决定的时候,代码方式可能更合适。

先看一个简单的Demo:

例子1:新建一个Single View Application template项目Demo4,在rootView上添加一个绿颜色的view,使新添加的view四个边距离superView四边20点宽

效果如图:

使用storyboard来实现这个效果很简单,选中绿色view,然后添加4个相对于superview的边界约束,约束的数值设置为20,然后Update Frame就可以了,因为不区分iOS设备,所以size class可以设置为默认的wAny hAny。Demo下载

接下来使用代码来实现UI布局,目前有3种方法可以使用:(1)最基本的约束实现方式;(2)特殊格式化语言的约束实现方式;(3)第三方UIView-AutoLayout

(1)最基本的约束实现方式

1.<span style="font-size:12px;">- (void)viewDidLoad {  
2. [super viewDidLoad];
3. // Do any additional setup after loading the view, typically from a nib.
4. self.view.translatesAutoresizingMaskIntoConstraints =NO;
5.
6. UIView *newView = [UIView new];
7. newView.backgroundColor = [UIColor greenColor];
8. [self.view addSubview:newView];
9.
10. newView.translatesAutoresizingMaskIntoConstraints =NO;
11.
12. NSLayoutConstraint *constraint = nil;
13.
14. constraint = [NSLayoutConstraint constraintWithItem:newView
15. attribute:NSLayoutAttributeLeading
16. relatedBy:NSLayoutRelationEqual
17. toItem:self.view
18. attribute:NSLayoutAttributeLeading
19. multiplier:1.0f
20. constant:20];
21. [self.view addConstraint:constraint];
22.
23. constraint = [NSLayoutConstraint constraintWithItem:newView
24. attribute:NSLayoutAttributeTrailing
25. relatedBy:NSLayoutRelationEqual
26. toItem:self.view
27. attribute:NSLayoutAttributeTrailing
28. multiplier:1.0f
29. constant:-20];
30. [self.view addConstraint:constraint];
31.
32. constraint = [NSLayoutConstraint constraintWithItem:newView
33. attribute:NSLayoutAttributeTop
34. relatedBy:NSLayoutRelationEqual
35. toItem:self.view
36. attribute:NSLayoutAttributeTop
37. multiplier:1.0f
38. constant:20];
39. [self.view addConstraint:constraint];
40.
41. constraint = [NSLayoutConstraint constraintWithItem:newView
42. attribute:NSLayoutAttributeBottom
43. relatedBy:NSLayoutRelationEqual
44. toItem:self.view
45. attribute:NSLayoutAttributeBottom
46. multiplier:1.0f
47. constant:-20];
48. [self.view addConstraint:constraint];
49.
50.}</span>

(2)特殊格式化语言的约束实现方式

1.<span style="font-size:12px;">- (void)viewDidLoad {  
2. [super viewDidLoad];
3. self.view.translatesAutoresizingMaskIntoConstraints =NO;
4.
5. UIView *newView = [UIView new];
6. newView.backgroundColor = [UIColor greenColor];
7. [self.view addSubview:newView];
8. newView.translatesAutoresizingMaskIntoConstraints =NO;
9.
10. NSMutableArray *constraintArray = [NSMutableArray array];
11.
12. [constraintArray addObjectsFromArray: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[newView]-20-|"
13. options:0
14. metrics:nil
15. views:NSDictionaryOfVariableBindings(newView, self.view)]];
16. [constraintArray addObjectsFromArray: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-20-[newView]-20-|"
17. options:0
18. metrics:nil
19. views:NSDictionaryOfVariableBindings(newView, self.view)]];
20. [self.view addConstraints:constraintArray];
21.}</span>

(3)第三方UIView-AutoLayout

1.<span style="font-size:12px;">- (void)viewDidLoad {  
2. [super viewDidLoad];
3. self.view.translatesAutoresizingMaskIntoConstraints =NO;
4.
5. UIView *newView = [UIView new];
6. newView.backgroundColor = [UIColor greenColor];
7. [self.view addSubview:newView];
8. newView.translatesAutoresizingMaskIntoConstraints =NO;
9.
10. [newView autoPinEdgeToSuperviewEdge:ALEdgeLeading withInset:20.0f];
11. [newView autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:20.0f];
12. [newView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:20.0f];
13. [newView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:20.0f];
14.}</span>

以上3种方式都实现了我们想要的效果,看来代码实现autolayout也不是那么复杂!

例子2:通过上边例子我们实现一个简单的UI布局,下面来一个稍微复杂点的,把上一篇中提到3个view布局的那个例子用代码布局实现一下,但难度有所增加,当size class切换的时候,页面布局发生相应的改变,效果如图:

首先初始化3个View:

1.<span style="font-size:12px;">- (UIView *) alView {  
2. UIView *newView = [UIView new];
3. newView.translatesAutoresizingMaskIntoConstraints =NO;
4.
5. return newView;
6.}
7.UIView *greenView = [self alView];
8.greenView.backgroundColor = [UIColor greenColor];
9.[self.view addSubview:greenView];
10.UIView *yellowView = [self alView];
11.yellowView.backgroundColor = [UIColor yellowColor];
12.[self.view addSubview:yellowView];
13.UIView *blueView = [self alView];
14.blueView.backgroundColor = [UIColor blueColor];
15.[self.view addSubview:blueView];</span>

接下来适配竖屏的约束:

1.<span style="font-size:12px;">- (NSMutableArray *) 
portraitConstraints:(UIView *)greenView :(UIView *)yellowView :(UIView *)blueView  
2.{
3. NSMutableArray *constraintArray = [NSMutableArray array];
4.
5. [constraintArray addObjectsFromArray:[NSLayoutConstraint
6. constraintsWithVisualFormat:@"H:|-20-[greenView]-20- [yellowView(==greenView)]-20-|" options:0 metrics:nil
7. views:NSDictionaryOfVariableBindings(greenView, yellowView)]];
8. [constraintArray addObjectsFromArray:[NSLayoutConstraint
9. constraintsWithVisualFormat:@"V:|-20-[greenView]-20- [blueView(==greenView)]-20-|" options:0 metrics:nil
10.views:NSDictionaryOfVariableBindings(greenView, blueView)]];
11.
12. [constraintArray addObjectsFromArray:[NSLayoutConstraint
13. constraintsWithVisualFormat:@"V:|-20-[yellowView]-20- [blueView(==yellowView)]-20-|" options:0 metrics:nil
14. views:NSDictionaryOfVariableBindings(yellowView, blueView)]];
15.
16. [constraintArray addObjectsFromArray:[NSLayoutConstraint
17. constraintsWithVisualFormat:@"H:|-20-[blueView]-20-|" options:0 metrics:nil
18. views:NSDictionaryOfVariableBindings(blueView)]];
19.
20. return constraintArray;
21.}</span>

然后横屏的约束:

1.<span style="font-size:12px;">- (NSMutableArray *)
 landscapeConstraints:(UIView *)greenView :(UIView *)yellowView :(UIView *)blueView  
2.{
3. NSMutableArray *constraintArray = [NSMutableArray array];
4.
5. [constraintArray addObjectsFromArray:[NSLayoutConstraint
6. constraintsWithVisualFormat:@"H:|-20-[greenView]-20- [yellowView(==greenView)]-20-|" options:0 metrics:nil
7. views:NSDictionaryOfVariableBindings(greenView, yellowView)]];
8.
9. [constraintArray addObjectsFromArray:[NSLayoutConstraint
10. constraintsWithVisualFormat:@"V:|-20-[blueView]-20- [greenView(==blueView)]-20-|" options:0 metrics:nil
11. views:NSDictionaryOfVariableBindings(greenView, blueView)]];
12.
13. [constraintArray addObjectsFromArray:[NSLayoutConstraint
14. constraintsWithVisualFormat:@"V:|-20-[blueView]-20- [yellowView(==blueView)]-20-|" options:0 metrics:nil
15. views:NSDictionaryOfVariableBindings(yellowView, blueView)]];
16.
17. [constraintArray addObjectsFromArray:[NSLayoutConstraint
18. constraintsWithVisualFormat:@"H:|-20-[blueView]-20-|" options:0 metrics:nil
19. views:NSDictionaryOfVariableBindings(blueView)]];
20.
21. return constraintArray;
22.}</span>

最后还要处理屏幕旋转:

1.<span style="font-size:12px;">-
 (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection  
2. withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
3.{
4. [super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator];
5.
6. [coordinator animateAlongsideTransition:^ (id <UIViewControllerTransitionCoordinatorContext> context) {
7. if (newCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact) {
8. NSLog(@"%s----%d", __FUNCTION__, __LINE__);
9. [self.view removeConstraints:self.view.constraints];
10. [self.view addConstraints: [self landscapeConstraints:self.greenView_ :self.yellowView_ :self.blueView_]];
11. } else {
12. NSLog(@"%s----%d", __FUNCTION__, __LINE__);
13. [self.view removeConstraints:self.view.constraints];
14. [self.view addConstraints: [self portraitConstraints:self.greenView_ :self.yellowView_ :self.blueView_]];
15. }
16. [self.view setNeedsLayout];
17. } completion:nil];
18.}</span>

这样就实现了我们预期的效果,总结下来,auotlayout就是给view添加足够的约束,让view系统可以根据约束来计算出一个view的frame。动手练习一下吧!

2、view动画在autolayout实现

当布局发生改变时,相当于对子view进行重新布局,而子view重新布局调用 layoutIfNeeded,所以动画可以这样实现:

1.<span style="font-size:12px;">- (void)animateConstraints  
2.{
3. [UIView animateWithDuration:0.5 animations:^{
4. [self.view layoutIfNeeded];
5. }];
6.}</span>

Github上已经有Demo了!

3、autolayout有没有局限性和解决不了的问题?兼容性怎么样?效率怎么样?

autolayout对view transforms支持的不好,这里有帖子详细描述了这个问题。

至于兼容性,只从iOS6就已经提出了autolayout的概念,现在iOS5系统不是很多了,甚至iOS6系统都已经升级为iOS7,未来一段时间大部分用户应该是使用iOS7和iOS8系统,所以兼容性问题不会太大,但size class是iOS8才有的概念,所以还有有一定的适配工作量。

效率话题这里有提到,有时间再细研究。

结束语:时间和体力总是有限的,标题是autolayout详解,可想达到详解还需要更多的时间去实践和总结,还有一些细节没有体现出来:

例如:

1.<span style="font-size:12px;">
[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[newView]-20-|
"options:0 metrics:nil views:NSDictionaryOfVariableBindings(newView, self.view)]</span>

1、这其中各个参数的含义,另外约束还有个优先级的概念

2、@"H:|-20-[newView]-20-|" 这种可视化布局字符串的含义等等,有空再补充了!

   
2200 次浏览       19
 
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
 
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试
最新活动计划
LLM大模型应用与项目构建 12-26[特惠]
QT应用开发 11-21[线上]
C++高级编程 11-27[北京]
业务建模&领域驱动设计 11-15[北京]
用户研究与用户建模 11-21[北京]
SysML和EA进行系统设计建模 11-28[北京]

android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   

Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合

领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...