UIView.frame的骗局
2016-01-04 10:15
375 查看
如果你刚刚开始接触IOS编程, 刚刚接触UIKit, 肯定会被 frame, bounds, center, layer.anchorPoint, layer.position 这些乱七八糟得属性折腾得心烦意乱. 并且,聪明的你肯定早就发现,这些属性并不是独立的, 比如frame和bounds, 你改变一个必然会影响另一个, 这就更加大了理解难度. 我想通过这篇浅显的日志,和一个简单的Demo来表达出我对这些变量的理解. 难免有偏差之处, 欢迎拍砖. 但是我能保证的是这些理解方式是实用的. 我个人也是看过网上很多日志对其有些微理解,
然后又通过写一个Demo来证明自己的想法.
其实, 受过10几年教育的你, 必然知道, 一个二维矩形, 只要有了{x,y,width,height}, 也就唯一确定了它的几何属性. 没错, 其实UIView里面也就这几个变量. 其他变量, 比如frame,bounds都是这些变量通过基本变量导出的.那么UIView拥有的真正意义上的属性有哪些呢?
bounds:
bounds是一个CGRect. 他的size部分决定了UIView的大小,也就是,bounds.width和bounds.height决定了UIView的大小.你也可以说bounds.width和bounds.height就是UIView的width和height. bounds.x和bounds.y决定了UIView的subView的原点坐标.如果你更改了bounds.x或者bounds.y,UIView的位置和大小完全不为所动, 但是UIView的所有subView都会平移一段距离(-bounds.x,-bounds.y)(这一点我们会在下文做详细陈述).
center:
望文生义(注意,这是个带贬义的词),他就是UIView的中心,也就是坐标点(view.width/2,view.height/2).但是,可恶的但是, 上句话仅仅在在一个UIView刚被创建的时候成立. 也就是,在刚刚创建UIView的时候,他恰好成立. 其实, center有它更重要的角色: 就是决定了UIView的位置. (但是,这个位置并不是我们常规意义上理解的(x,y). 在这里你先知道它来决定我们UIView的位置就好了.)
下面来看我们的第一个公式.
我想,对于程序员的你,没有比比代码更直接的方式了吧?
下面就是UIView的属性frame的实现:
下面来到实战演习:
初始状态,我们新建一个UIView,设置frame为(100,100,50,50):
图1
![](http://www.jcodecraeer.com/uploads/20150204/1423037476889122.png)
然后,我们改变frame
我们再看看各个属性的变化:
图2
![](http://www.jcodecraeer.com/uploads/20150204/1423037526844799.png)
没错,你看到了我们刚刚提到的center属性. 我们刚刚说过, 这个属性主要决定了UIView的位置. 所以当我们在setFrame:的时候会改变UIView的位置.
好吧, 既然我们见过center先生, 那就给您介绍一下吧, 也要给点面子是不?
center是UIView的相关属性中主要决定UIView位置(跟大小相对), 我们在图2的基础上, 改变center:
我们来看一下效果:
图3
![](http://www.jcodecraeer.com/uploads/20150204/1423037562727925.png)
看到了吧? testView的位置向左下移动了(125-20, 125-20)距离. 根据代码1, 我们可以看到, center和bounds属性是相互独立的. 也就是他们中间某一个发生了变化, 不会影响另一个. 这说明了什么? 说明我们改变center的时候, 仅仅会改变testView的位置, 而它的大小不会有任何改变.
好了, 到这里, testView的位置和大小问题,我们已经彻底解决了. 但是对于bounds小伙儿, 我们只关注了它的size部分, 忽略了他的origin部分. 不好意思, bounds小伙, 现在才想起你.
没图没真相, 我们首先来点料吧:
图4,图5
![](http://www.jcodecraeer.com/uploads/20150204/1423037563624563.png)
![](http://www.jcodecraeer.com/uploads/20150204/1423037564118423.png)
bounds.origin初始情况下为(0,0). 我们设置
得到左图. 我们没有改变bounds.size(仍然是40,40), 只是修改了bounds.origin: 从(0,0)改变成(25,25).我们发现testView内部的小红点移动了(0-25,0-25)距离.(至于为设么这里是-25而不是25, 我也还没理解, 望高人指点.) 反正, 知道当你改变bounds.origin的时候, testView内部所有的subView都要想做相反方向的位移就对了.
在左图的基础上,再设置
我们得到右图.
需要注意的是, 这里所有subView的frame是不会跟着改的, 还是原来的值. 我们可以这么理解: 设置testView.origin, 会改变所有孩子节点位置的基准点. 就比如, 我们把一辆车平移了, 我们站在路边发现车里的方向盘和发动机等子组件的位置都改变了, 而方向盘发动机等"子组件"相对于汽车的坐标没有改变.
关于bounds, 还有一点要说: 当你的subView的某些部分落在了bounds定义的矩形之外, 那么这些落在矩形之外的部分, 便不能接受任何点击,踩踏,横扫等事件了....
frame的定义并没有这么简单, 因为还搀插着第三者的关系: testView.layer.anchorPoint.
有请 anchorPoint 出场!
好了,我们重新定义frame的getter/setter函数(其实就是把代码1的定义中所有的1/2改为view.layer.anchorPoint):
因为anchorPoint的默认值是(0.5,0.5),所以如果你不改变anchorPoint,那么代码1就是正确的. 之所以在代码1里撒了一个谎, 是因为不想那么早把anchorPoint引出来.
现在,你既然知道了anchorPoint跟frame之间的关系, 必然想知道它到底有什么用:
先给你一个直观印象: anchorPoint就是一个钉子,把一幅画钉在墙上. 以后你想做什么转动也好, 把相框拉伸也好, 这个点是绝对不会动的.
anchorPoint的默认值是(0.5,0.5). 也就是说默认情况下,你对testView作旋转和缩放, 都会以(bounds.size.width/2,bounds.size.height/2)为基准点.下面我们接着图5来看一个转化:
视图如下:
图6
![](http://www.jcodecraeer.com/uploads/20150204/1423037753934564.png)
我们看到,testView以中心点位固定点,等比例扩大了一倍.frame.origin也移动了(-20,-20). 跟我们的预期一样.
这里需要特别提醒各位的是: 当对testView进行了transform之后,我们再去设置frame, frame已经完全不理咱们了. 也就是说setFrame函数完全不工作了. 我们这个时候调用frame的getter函数, 得到的是transform后的大小.如上图所示,变成了(85,85,80,80). 如果, 我们把scale恢复为1, 再改变anchorPoint的位置为(0,0), 然后再把testView放大一倍(scale=2). 看看会发现什么:
下面三个图分别对应上面三行代码执行后的状态:
图7,图8,图9
![](http://www.jcodecraeer.com/uploads/20150204/1423037806870317.png)
![](http://www.jcodecraeer.com/uploads/20150204/1423037808263311.png)
![](http://www.jcodecraeer.com/uploads/20150204/1423037808994077.png)
对于上面的运行结果, 我有几点说明:
当我们改变anchorPoint的时候,center没有改变,那么根据代码7, frame也会随着发生改变. 您可以在图8中观察到这一变化.
观察图8和图9的变化,你可以发现,这次缩放的中心点在左上角. 因为我们设置了anchorPoint = CGPointMake(0,0).
如果您是对图像做旋转, anchorPoint也是旋转的中心.
到此为止, 文章开始提到的属性都基本讲完了. 只剩下了layer.position. 如果你细心, 你会发现上面所有的图片中, layer.position === center, 没错, 任何时候他们都是相等的.
这里有博客中Demo的代码下载, 本博客中的所有截图都来自于Demo的截屏.如果我在博客中没有说明白, 您可以下载Demo仔细把玩. 相信你可以在实际操作中有更深刻的理解
Demo底部的输入框支持的的语法有:
比如您输入frame =(0,0,40,40),就相当于执行代码testView.frame = CGRectMake(0,0,40,40). 输入center = (50,50)就相当于执行代码testView.center = CGPointMake(50,50).
点此下载Demo
然后又通过写一个Demo来证明自己的想法.
其实, 受过10几年教育的你, 必然知道, 一个二维矩形, 只要有了{x,y,width,height}, 也就唯一确定了它的几何属性. 没错, 其实UIView里面也就这几个变量. 其他变量, 比如frame,bounds都是这些变量通过基本变量导出的.那么UIView拥有的真正意义上的属性有哪些呢?
UIView 真正意义上的属性:
bounds:bounds是一个CGRect. 他的size部分决定了UIView的大小,也就是,bounds.width和bounds.height决定了UIView的大小.你也可以说bounds.width和bounds.height就是UIView的width和height. bounds.x和bounds.y决定了UIView的subView的原点坐标.如果你更改了bounds.x或者bounds.y,UIView的位置和大小完全不为所动, 但是UIView的所有subView都会平移一段距离(-bounds.x,-bounds.y)(这一点我们会在下文做详细陈述).
center:
望文生义(注意,这是个带贬义的词),他就是UIView的中心,也就是坐标点(view.width/2,view.height/2).但是,可恶的但是, 上句话仅仅在在一个UIView刚被创建的时候成立. 也就是,在刚刚创建UIView的时候,他恰好成立. 其实, center有它更重要的角色: 就是决定了UIView的位置. (但是,这个位置并不是我们常规意义上理解的(x,y). 在这里你先知道它来决定我们UIView的位置就好了.)
下面来看我们的第一个公式.
揭开frame的本质
我想,对于程序员的你,没有比比代码更直接的方式了吧?下面就是UIView的属性frame的实现:
图1
![](http://www.jcodecraeer.com/uploads/20150204/1423037476889122.png)
然后,我们改变frame
图2
![](http://www.jcodecraeer.com/uploads/20150204/1423037526844799.png)
没错,你看到了我们刚刚提到的center属性. 我们刚刚说过, 这个属性主要决定了UIView的位置. 所以当我们在setFrame:的时候会改变UIView的位置.
好吧, 既然我们见过center先生, 那就给您介绍一下吧, 也要给点面子是不?
center如何搞定了位置?
center是UIView的相关属性中主要决定UIView位置(跟大小相对), 我们在图2的基础上, 改变center:图3
![](http://www.jcodecraeer.com/uploads/20150204/1423037562727925.png)
看到了吧? testView的位置向左下移动了(125-20, 125-20)距离. 根据代码1, 我们可以看到, center和bounds属性是相互独立的. 也就是他们中间某一个发生了变化, 不会影响另一个. 这说明了什么? 说明我们改变center的时候, 仅仅会改变testView的位置, 而它的大小不会有任何改变.
好了, 到这里, testView的位置和大小问题,我们已经彻底解决了. 但是对于bounds小伙儿, 我们只关注了它的size部分, 忽略了他的origin部分. 不好意思, bounds小伙, 现在才想起你.
bounds的另一半
没图没真相, 我们首先来点料吧:图4,图5
![](http://www.jcodecraeer.com/uploads/20150204/1423037563624563.png)
![](http://www.jcodecraeer.com/uploads/20150204/1423037564118423.png)
bounds.origin初始情况下为(0,0). 我们设置
在左图的基础上,再设置
需要注意的是, 这里所有subView的frame是不会跟着改的, 还是原来的值. 我们可以这么理解: 设置testView.origin, 会改变所有孩子节点位置的基准点. 就比如, 我们把一辆车平移了, 我们站在路边发现车里的方向盘和发动机等子组件的位置都改变了, 而方向盘发动机等"子组件"相对于汽车的坐标没有改变.
关于bounds, 还有一点要说: 当你的subView的某些部分落在了bounds定义的矩形之外, 那么这些落在矩形之外的部分, 便不能接受任何点击,踩踏,横扫等事件了....
#ifdefine 欺骗 明明知道某个事实,却故意隐瞒或窜改并加以传播 写到这里, 我其实要跟大家道一个歉, 因为我在上面对frame的定义欺骗了大家. #endif
frame的定义并没有这么简单, 因为还搀插着第三者的关系: testView.layer.anchorPoint.
有请 anchorPoint 出场!
在墙上钉个钉子,就是anchorPoint
好了,我们重新定义frame的getter/setter函数(其实就是把代码1的定义中所有的1/2改为view.layer.anchorPoint):现在,你既然知道了anchorPoint跟frame之间的关系, 必然想知道它到底有什么用:
先给你一个直观印象: anchorPoint就是一个钉子,把一幅画钉在墙上. 以后你想做什么转动也好, 把相框拉伸也好, 这个点是绝对不会动的.
anchorPoint的默认值是(0.5,0.5). 也就是说默认情况下,你对testView作旋转和缩放, 都会以(bounds.size.width/2,bounds.size.height/2)为基准点.下面我们接着图5来看一个转化:
图6
![](http://www.jcodecraeer.com/uploads/20150204/1423037753934564.png)
我们看到,testView以中心点位固定点,等比例扩大了一倍.frame.origin也移动了(-20,-20). 跟我们的预期一样.
这里需要特别提醒各位的是: 当对testView进行了transform之后,我们再去设置frame, frame已经完全不理咱们了. 也就是说setFrame函数完全不工作了. 我们这个时候调用frame的getter函数, 得到的是transform后的大小.如上图所示,变成了(85,85,80,80). 如果, 我们把scale恢复为1, 再改变anchorPoint的位置为(0,0), 然后再把testView放大一倍(scale=2). 看看会发现什么:
图7,图8,图9
![](http://www.jcodecraeer.com/uploads/20150204/1423037806870317.png)
![](http://www.jcodecraeer.com/uploads/20150204/1423037808263311.png)
![](http://www.jcodecraeer.com/uploads/20150204/1423037808994077.png)
对于上面的运行结果, 我有几点说明:
当我们改变anchorPoint的时候,center没有改变,那么根据代码7, frame也会随着发生改变. 您可以在图8中观察到这一变化.
观察图8和图9的变化,你可以发现,这次缩放的中心点在左上角. 因为我们设置了anchorPoint = CGPointMake(0,0).
如果您是对图像做旋转, anchorPoint也是旋转的中心.
到此为止, 文章开始提到的属性都基本讲完了. 只剩下了layer.position. 如果你细心, 你会发现上面所有的图片中, layer.position === center, 没错, 任何时候他们都是相等的.
结尾
这里有博客中Demo的代码下载, 本博客中的所有截图都来自于Demo的截屏.如果我在博客中没有说明白, 您可以下载Demo仔细把玩. 相信你可以在实际操作中有更深刻的理解Demo底部的输入框支持的的语法有:
点此下载Demo
相关文章推荐
- <supports-screens android:anyDensity="true" />
- UIScrollView的属性总结
- master password is required to unlock the password database.the password database will be unlocked d
- 为OLED屏增加GUI支持6:进度条控件
- Java中的String,StringBuffer,StringBuilder的区别
- UGUI脚本记录
- 轻量级ORM-Fluentdata入门
- UESTC 92 Journey(LCA)
- UIImageView裁剪成圆形的方法
- 关于VS2010 rcxdti.dll is unable to load rcxdtiui.dll加载失败问题的解决方案
- UITextField
- require.js的用法-阮一峰
- luerl 测试用例
- iOS_UITableView_不显示无内容部分的分割线
- 新手常忽视的问题,交互设计中的默认值!
- build up
- UITextView字数限制自适应高度限制输入
- UITextView自适应高度动态计算高度
- iOS中storyboard故事板使用Segue跳转界面、传值
- UISlider用法的简单整理