iOS进阶指南试读之UI篇
UI篇
UI是一个iOS开发工程师的基本功。
怎么说?
UI本质上就是你调用苹果提供给你的API来完成设计师的设计。
所以,想提升UI的功力也很简单,没事就看看UIKit里的各个类的头文件。如果能做到烂熟于胸,相信会有很大的提升。
Autolayout
顾名思义,Autolayout = 自动+布局,也就是当你设置好一定的约束之后,系统会帮你处理布局的细节。
那么,在不那么自动的年代,我们用的是什么?
我们用的是Frame布局。
那么,先来讨论一下Frame布局有哪些问题?
举个简单的例子好了。
如图。
代码如下。
- (void)viewDidLoad {
[super viewDidLoad];
redView = [UIView new];
redView.frame = CGRectMake(, , , );
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView]; yellowView = [UIView new];
yellowView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
yellowView.frame = CGRectInset(redView.bounds, , );
yellowView.backgroundColor = [UIColor yellowColor];
[redView addSubview:yellowView];
// Do any additional setup after loading the view, typically from a nib.
}
图中黄色的View是红色View的子View,那么,如果我期望无论红色View变大还是变小,黄色View距离红色View的边距总是不变的,该怎么做呢?
一般来说有两种做法。
- 设置黄色View的
autoresizingMask
属性,设置为UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
,这样设置的结果是黄色View的宽高会随着父View宽高的改变而改变,但是不改变间距。 - 继承一个UIView,在UIView里的
layoutSubviews
里设置子View的宽高。如下。
- (void)layoutSubviews
{
yellowView.frame = CGRectInset(self.bounds, 20, 20);
[super layoutSubviews];
}
实际上这两种做法都是很麻烦而且不灵活的。例如autoresizingMask
对应的UIViewAutoresizing
模式只有6种,我们常常用到的居中对齐等完全没有。二是如果在layoutSubviews
里设置布局,又会造成如果父View有动画,那么会出现奇怪的动画效果。
所以,为了解除这些痛点,苹果公司为我们带来了Autolayout。
Autolayout的用法
在这里,我会假设看本书的都是会使用Autolayout基本功能的朋友。所以,我会直接讲解一些较为深入和偏实战的东西。
1. Intrinsic Content Size
思考一下,当你拉约束的时候,为什么UILabel和UIButton只需要拉能够确定坐标的约束即可,而不需要确定宽高的约束?比如说。
这个UILabel就是只确定了左边距和上边距,就可以了。但是如果我们放了一个UIView,只确定坐标而不指定大小,就会出错。例如。
Xcode会提示,这个View还需要指定宽和高。
造成这种情况的原因就是,有些View可以通过自己的内容计算宽高,而有些View不可以。这也是我们要讲的内容,也即我们的标题,Intrinsic Content Size
,翻译过来就是,固有内容大小。
对应的系统方法就是- (CGSize)intrinsicContentSize
要知道,Autolayout的本质无非就是系统通过你设置的约束来帮你计算一个控件的位置和大小。
所以,一个UILabel肯定具备的四个条件是,内容、字体、行数、换行模式。也就是说,只要我们赋予了Label内容,那么它的大小也就确定了。所以,我们不需要特意指定一个UIlabel的宽高,除非你有什么特殊要求。
那么UIView为什么不可以自己计算大小?
答案其实也可以猜到,因为他没有内容。
来看一个例子。
首先,我们在storyboard上放置一个UILabel,然后设置Leading和Trailing。如图。
运行模拟器,看一下效果。
我们会发现,这样设置的UILabel,它的大小总是会和内容大小刚好一致,但是如果我们期望UILabel的大小总是比内容宽高都大一些,也就是所谓的留白。比如这样。
那么,我们应该怎么做呢?
首先,我们创建一个继承于UILabel的自定义试图,然后重写
- (CGSize)intrinsicContentSize,这个方法。代码如下。 - (CGSize)intrinsicContentSize
{
CGSize originalSize = [super intrinsicContentSize]; CGSize size = CGSizeMake(originalSize.width+, originalSize.height+); return size;
}
上述代码的意思就是,我们先获取系统通过Label的内容计算出来的宽和高,再分别给他增大再返回新的Size就可以了。再运行一下,你就会发现,Label的大小就会比内容大了。(别忘了,把对齐方式设置为居中)
再回到之前的那个问题,UIView如果只设置坐标,不设置大小会报错的问题。
如果是用代码写约束,如果你只想设置坐标不想设置大小,那么你需要像上面的代码一样,在- (CGSize)intrinsicContentSize
为你的UIView指定一个默认大小。
如果是在XIB里,那么你需要在下图这个Instrinsic Size
的属性里设置为Placeholder
。这样,Xcode就不会报错了。
例子
其实看完上面的叙述,你会思考,到底什么情况下,一个UIView需要只设置坐标不设置大小呢?
其实这种场景相当普遍。比如,我们常常会碰到,一个View中有两个Label,两个Label的高度均和内容有关,这时候,你的View的高度就必须由两个Label的高度有关,而不能一开始就定死。例如。
一个已知宽度的UIView中,有两个UILabel,我希望这个UIView的高度由两个UIlabel的高度来确定。效果如下图。
也就是说,图中红色view的高度是和两个UIlabel相关联的。我们尝试来实现它。
首先,在storyboard上拉取一个UIView。设置背景色为红色。如图。
我们为这个红色View设置了3个约束,分别是。
- Leading space to SuperView:8
- Trailing space to SuperView:8
- Top Space to SuperView:8
也就是分别设置了View的左边距,右边距和上边距,熟悉约束的人应该知道,这时候View的约束是不够的。为什么?
因为,左边距和上边距确定了View的(x,y),然后左边距和右边距确定了这个View的宽度,我们缺少了Height。
但是这个Height,要由View内部的两个label来确定,为了让Xcode不再认为我们拉的约束有问题,再结合我们上面讲的Intrinsic Content Size
,我们可以在Xib的这个位置设置Intrinsic Size
为Placeholder
,这样,Xcode就认为这个View有默认的大小,所以就不会报错了。
然后,自然是放入两个UILabel了。如图。
为了label能够多行显示,别忘记设置lineofnumber为0.
还有一点需要注意的是,两个UILabel之间肯定是需要一个设置一个垂直约束的,否则整个View就没有办法确定自己的高度,思考一下这是为什么?
运行一下,看看结果。
成功了。
2. Content Hugging Priority和Content Compression Resistance
这两个概念需要结合我们上面讲的Intrinsic Content Size
来理解,每一个控件都有一个系统计算的最佳大小。
所以,Content Hugging Priority
这个属性就代表着,一个控件拒绝本身size大于InstrinsicSize
的优先级。
那么Content Compression Resistance
,这个属性代表着,一个控件拒绝本身size小于InstrinsicSize
的优先级。
这样子说还是有点抽象。那么我们来看一个例子好了。
我们在storyboard中创建了一个UITableView,并在其中创建了一个自定义的UITableViewCell。
这个自定义的UITableviewCell中有两个Label,都是多行显示的Label。然后给他们设定约束。
第一个Label设定的约束为:
- Leading Space:8
- Trailing Sace:8
- Top Space:8
- Bottom Space(距离下面的Label的间距): 9
第二个Label设定的约束为:
- Leading Space:8
- Trailing Sace:8
- Bottom Space:8
- Top Space(距离上面面的Label的间距): 9
设定好了,ok,好像没什么问题。如图。
但是,如果这时候你把UITableViewCell的高度扩大。看看是怎样的结果。如图。
Xcode报错了。为什么?
因为Cell的高度扩大,势必会影响两个Label的位置和大小,所以,现在Label面临着一个问题,在保持和父View(也就是UITableViewCell的contentView)间距不变的情况下,必须有一个Label是需要妥协的,怎么个妥协法呢?就是要扩大高度,扩大高度,就意味着比label本身的文字内容的高要大了。
那么,到底是两个Label中的哪一个label做出这个妥协呢?
Xcode并不知道,因为这个不知道,所以Xcode报错了。
话说回来了,Xcode为什么会不知道,就是因为两个Label的Content Hugging Priority
里面的Vertical
这个属性的值是一样的。因为两个Label拒绝变高的优先级相同,以至于Xcode不知道到底该拉伸哪个Label。所以,解决方案就是,把其中一个的改小。如图。
这样就可以了。那么,这样就结束了么?
如果这时候,你再把UITableViewCell的高度减小,会发生什么情况呢?Xcode又报错了。那么,这次的原因又是什么?
其实和上一个原因较为类似。
Cell的高度减小导致UILabel为了保持和父View间距不变,所以面临一个高度压缩的情况,那么到底谁压缩,Xcode依然不知道,因为两个Label的Content Compression Resistance
这个属性里的Vertical
优先级一样,和上面的解决办法一样只需要改小一个即可。
如果是手写Autolayout,在哪里写最好?
其实这个东西我在Reviewcode.cn里讨论过。但是鉴于可能会有人没有看过,并且对这个问题存疑,还是在这里说一下。
结论如下:
- 如果是在自定义view中,写在init方法中。
- 如果是在ViewController中,写在
- (void)viewDidLoad()
中。
为什么不能写在viewDidAppear
或者viewWillAppear
中?
因为这个东西和NSNotification
是一样的,你不能确定viewDidAppear
和viewWillAppear
调用的时机和调用的次序,不信的话,你可以用NSLog打印一下,并且使用手势在NavigationController中不停的左右滑动控制器,看看打印的结果。
但,viewDidLoad
是可以保证在整个生命周期只出现一次的。为了避免约束重复添加,所以你应该在viewDidLoad
中添加。
iOS进阶指南试读之UI篇的更多相关文章
- iOS设计指南
备忘:iOS设计指南:http://www.ui.cn/detail/32167.html
- 【读书笔记】读《高性能网站建设指南》及《高性能网站建设进阶指南:Web开发者性能优化最佳实践》
这两本书就一块儿搞了,大多数已经理解,简单做个标记.主要对自己不太了解的地方,做一些记录. 一.读<高性能网站建设指南> 0> 黄金性能法则:只有10%~20%的最终用户响应时间 ...
- iOS开发UI篇—CAlayer(自定义layer)
iOS开发UI篇—CAlayer(自定义layer) 一.第一种方式 1.简单说明 以前想要在view中画东西,需要自定义view,创建一个类与之关联,让这个类继承自UIView,然后重写它的Draw ...
- iOS开发UI篇—UITabBarController简单介绍
iOS开发UI篇—UITabBarController简单介绍 一.简单介绍 UITabBarController和UINavigationController类似,UITabBarControlle ...
- iOS开发UI篇—懒加载
iOS开发UI篇—懒加载 1.懒加载基本 懒加载——也称为延迟加载,即在需要的时候才加载(效率低,占用内存小).所谓懒加载,写的是其get方法. 注意:如果是懒加载的话则一定要注意先判断是否已经有了, ...
- iOS开发UI篇—CAlayer层的属性
iOS开发UI篇—CAlayer层的属性 一.position和anchorPoint 1.简单介绍 CALayer有2个非常重要的属性:position和anchorPoint @property ...
- iOS开发UI篇—CAlayer(创建图层)
iOS开发UI篇—CAlayer(创建图层) 一.添加一个图层 添加图层的步骤: 1.创建layer 2.设置layer的属性(设置了颜色,bounds才能显示出来) 3.将layer添加到界面上(控 ...
- iOS开发UI篇—CALayer简介
iOS开发UI篇—CALayer简介 一.简单介绍 在iOS中,你能看得见摸得着的东西基本上都是UIView,比如一个按钮.一个文本标签.一个文本输入框.一个图标等等,这些都是UIView. 其实 ...
- iOS开发UI篇—核心动画(UIView封装动画)
iOS开发UI篇—核心动画(UIView封装动画) 一.UIView动画(首尾) 1.简单说明 UIKit直接将动画集成到UIView类中,当内部的一些属性发生改变时,UIView将为这些改变提供动画 ...
随机推荐
- 阿里云ECS 利用快照创建磁盘实现无损扩容数据盘
在扩容数据盘时,若遇到磁盘原因导致无法无损的扩容时,可以临时购买一块独立云磁盘来存放数据,然后将数据盘彻底格式化来解决,以下是操作步骤: 1. 首先基于当前数据盘创建一个快照,备份数据,同时可以利用 ...
- Swift3 CADisplayLink简单用法
1.定义属性 var displayLinkTimer:CADisplayLink? = nil 2.init displayLinkTimer = CADisplayLink(target: sel ...
- Vue.js——60分钟快速入门 开发· webpack 中文文档
转载于:http://www.cnblogs.com/keepfool/p/5619070.html http://www.css88.com/doc/webpack2/guides/get-star ...
- 树莓派进阶之路 (007) - 树莓派安装cmake3.5 脚本(原创)
cmake3.5安装脚本: #!/bin/bash cd echo "安装make" sudo apt-get install make echo "安装cmake&qu ...
- 【php】thinkphp以post方式查询时分页失效的解决方法
好久没有写博客了,最近说实话有点忙,各个项目都需要改bug.昨天晚上一直没有解决的php项目中的bug,就在刚才终于搞定,在这里还需要感谢博客园大神给的帮助! 具体问题描述 最近遇到一个非常棘手的问题 ...
- 关于VC++的增量链接(Incremental Linking)
增量链接(Incremental Linking)这个词语在使用Visual C++时经常会遇到(其实不只是VS系列,其它链接器也有这个特性), 就比如经常遇到的:上一个增量链接没有生成它, 正在执行 ...
- mysql快速移植表数据
使用select into outfile xxx , load data infile xxx 例如 : SELECT * into outfile '/tmp/out.txt' FROM `db ...
- iOS 优秀开源框架 开源包 开发包 from : Podfile of chatsecure ---待完善
前段时间发现chatsecure的podfile中使用了很多非常优秀的开源包 和 大公司的sdk. 拿出来分享下. 各个类库的作用待完善. platform :ios, "7.0" ...
- ldconfig命令
ldconfig是一个动态链接库管理命令 为了让动态链接库为系统所共享,还需运行动态链接库的管理命令--ldconfigldconfig 命令的用途,主要是在默认搜寻目录(/lib和/usr/lib) ...
- Scipy:高端科学计算
转 :https://blog.csdn.net/lwfcgz/article/details/23290623 Scipy scipy包包含致力于科学计算中常见问题的各个工具箱.它的不同子模块相应于 ...