起因
大家知道, 我们可以设置 view 的四个角或者其中一个或者几个为圆角.
使用的方法:
1 | + (instancetype)bezierPathWithRoundedRect:(CGRect)rect |
拖好界面元素之后, 在代码中来修改其为圆角, 居然失败了.
想要的效果是这样的:
但是最终是这样的:
于是总结了一下, 分享给大家.
设置圆角
这里分两种情况.
第一种: 只放置控件, 不设置约束.
1.storyboard 中拖好控件.
注意: 这里我并没有设置任何约束.
2.vc 代码
1 | @interface ViewController () |
代码编译运行到模拟器(iphone6), 看不到任何东西.
log 日志显示 lb 的信息如下:
1 | [ViewController viewDidLoad]: |
位置信息是正确的, 咨询检查发现是参数传入错误, 修改 changeLbCorner 方法:
1 | - (void)changeLbCorner |
这里只是将 self.lb.frame
改成了 self.lb.bounds
.
再次运行可以看到效果:
第二种: 放置控件并设置约束.
1.设置 lb 距离父 view 左边和上边的约束.
2.运行上面的代码, 发现, lb 并没有被设置为圆角.
并且 lb 的宽度和高度变小了, 变成了文字的实际的宽高.
看 log:
1 | [ViewController viewDidLoad]: |
其实这个时候, (从上面图中可以看出) 这里的信息是错误的.
正确的信息应该是这样的(在 viewDidAppear 中)打印信息:
1 | [ViewController viewDidAppear]: |
于是, 将
1 | [self changeLbCorner]; |
放到 viewDidAppear 中, 圆角就正常了.
接下来, 我们把 lb 的宽高(136*39)约束也加上.
看一下, viewDidLoad 和 viewDidAppear 方法中打印的信息:
1 | [ViewController viewDidLoad] |
可以看出, viewDidLoad 中错的一塌糊涂.
这里也说明一个问题:
xib 或者 storyboard 中设置过约束(现实开发中, 基本都会设置约束)的组件, 在 viewDidLoad 中并没有完全 layout, 只是预加载了这些组件.
想获取组件如 frame 何 bounds 信息, 在 viewDidLoad 中是不合适甚至是错误的.
那么, 问题来了, 哪里合适哪里正确.
上面如果你认真看了, 在 viewDidAppear 中是可以正确获取的, 那么还有没有其他方法可以获取呢?
VC 生命周期函数
要回答上面的问题, 大家要知道 vc 的生命周期函数.
上面的例子, 可以看出: 当函数 ViewDidLoad 被调用的时候,IBQutlets 已经被连接,但View 还没有被加载出来,无法获取 frame 等信息.
可以在 viewDidLoad 中完成在 IB 中不能完成的 view 的自定义。
关于 loadView 和 viewDidLoad 在后面博客跟大家分享.
今天要说的是
1 | viewDidLayoutSubviews |
viewDidLayoutSubviews
在 VC 子视图位置或者尺寸 (position|size) 被改变的时候被调用.
直到 AutoLayout 已经完成工作的时候才会被确定,所以在执行完 AutoLayout 之后会调用此方法. 换句话说, view 的 frame 和 bounds 这个时候是正确可以获取的.
viewDidLayoutSubviews
这个方法在 viewDidAppear
之前被调用, 有可能会被调用多次.
即依赖 bounds 或者 frame 的操作,都应该放在viewDidLayoutSubviews
中,而不是 viewDidLoad
或 viewWillAppear
中.
改变后的代码如下:
1 | #import "ViewController.h" |
打印的 log 如下:
1 | [ViewController viewDidLoad] |
frame 和 bounds
上面的例子, 大家看到由于传入了 frame 而不是 bounds 造成设置圆角失败.
下面说说 frame 和 bounds.
概念
从网上”偷”过来的图
1.frame
该 view 在父 view 坐标系统中的位置和大小(参照点是,父坐标系统).
2.bounds
该 view 在本地坐标系统中的位置和大小(参照点是,本地坐标系统,就相当于 view 自己的坐标系统,以(0,0)点为起点).
其实本地坐标系统的关键就是要知道的它的原点(0,0).
bounds 默认值是(0, 0, width, height).除非手动改变 bounds 的值.
单纯的从概念上面, 很难理解二者的区别.
提供一个例子, 例子大概是这样的:
redView 是 yellowView 的父视图, yellowView 是 blueView 的父视图.
通过改变 redView 的 bounds 会影响子视图的位置(不是frame).
将 redView 的 bounds 起点设为(-20, -20), 子视图相对于 redView 的本地坐标(0, 0), 也就需要往下增加20, 这样, yellowView 就往下移动了.
效果图:
完整代码
1 | #import "ViewController.h" |
总结
- frame, 描述的是当前视图在其父视图中的位置和大小.
bounds, 描述的是当前视图在其自身坐标系统中的位置和大小.
所以, bounds 默认是 (0, 0, frame.size.width, frame.size.height)
另外, 还有一个 center
描述的是当前视图的中心点在其父视图中的位置.
- bounds 和 frame 是两个不等同的概念, 改变 bounds 会影响子视图的位置(人眼看到其改变了位置), 设置 bounds 可以修改自己坐标系的原点位置. 但是不会改变子视图的 bounds 和 frame.
明白上面的道理很重要, iOS 中滚动视图能让你看到其中的内容, 正是利用了 contentoffset 和 bounds 属性.
这里以 tableView 为例子, 当我们向上滚动 tableView, tableView 的 contentOffset 和 bounds 的坐标都是正数, 相当于其本地坐标(0, 0)改变了即增加了(坐标系往下为增加), 那么其子视图就会向上去.
向下滑动时, 也是同一个道理.
可以通过运行 完整 Demo 中[查看 TableView]按钮来打开例子, 看日志.
改变子视图所有父视图的 bounds, 子视图的位置是累加改变的.
如上面改变 redView 和 yellowView 的 bounds, blueView 的位置相对 redView 往下移动了 40.当同一个视图的 bounds 大于 frame, 会导致 frame 被撑大,frame 的 x, y, width, height 都会被改变. 反之, bounds 小于 frame, frame 也会变小.
附录
GitHub 上面可以下载 完整 Demo
推荐之前写在 CSDN 上的博文: iOS UI 技巧: 视图无法被点击
可关注我的微信公众号: