UIView.frame的骗局
如果你刚刚开始接触IOS编程, 刚刚接触UIKit, 肯定会被 frame, bounds, center, layer.anchorPoint, layer.position
这些乱七八糟得属性折腾得心烦意乱. 并且,聪明的你肯定早就发现,这些属性并不是独立的, 比如frame
和bounds
, 你改变一个必然会影响另一个, 这就更加大了理解难度. 我想通过这篇浅显的日志,和一个简单的Demo来表达出我对这些变量的理解. 难免有偏差之处, 欢迎拍砖. 但是我能保证的是这些理解方式是实用的. 我个人也是看过网上很多日志对其有些微理解, 然后又通过写一个Demo来证明自己的想法.
转载请注明出处: http://www.cnblogs.com/jhzhu/
其实, 受过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
-(CGRect) frame
{
float x = center.x - 1/2 * bounds.width;
float y = center.y - 1/2 * bounds.height;
float width = bounds.width;
float height = bounds.height;
return CGRectMake(x, y, width, height);
}
-(void) setFrame:(CGRect) rect
{
center.x = rect.x + 1/2 * rect.width;
center.y = rect.y + 1/2 * rect.height;
bounds.width = rect.width;
bounds.height = rect.height;
}
下面来到实战演习:
//代码2
- (void)viewDidLoad
{
[super viewDidLoad];
testView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 50, 50)];
testView.backgroundColor = [UIColor blueColor];
[self.view insertSubview:testView atIndex:0];
UIView* innerView = [[UIView alloc] initWithFrame:CGRectMake(23,23, 4, 4)];
innerView.backgroundColor = [UIColor redColor];
[testView addSubview:innerView];
}
初始状态,我们新建一个UIView
,设置frame
为(100,100,50,50)
:
图1
然后,我们改变frame
testView.frame = CGRectMake(0,0,40,40); //代码3
我们再看看各个属性的变化:
图2
没错,你看到了我们刚刚提到的center
属性. 我们刚刚说过, 这个属性主要决定了UIView
的位置. 所以当我们在setFrame:
的时候会改变UIView
的位置.
好吧, 既然我们见过center
先生, 那就给您介绍一下吧, 也要给点面子是不?
center如何搞定了位置?
center
是UIView
的相关属性中主要决定UIView
位置(跟大小相对), 我们在图2的基础上, 改变center:
testView.center = CGPointMake(125,125);//代码4
我们来看一下效果:
图3
看到了吧? testView
的位置向左下移动了(125-20, 125-20)
距离.
根据代码1, 我们可以看到, center
和bounds
属性是相互独立的. 也就是他们中间某一个发生了变化, 不会影响另一个. 这说明了什么? 说明我们改变center
的时候, 仅仅会改变testView
的位置, 而它的大小不会有任何改变.
好了, 到这里, testView
的位置和大小问题,我们已经彻底解决了.
但是对于bounds
小伙儿, 我们只关注了它的size
部分, 忽略了他的origin
部分. 不好意思, bounds
小伙, 现在才想起你.
bounds的另一半
没图没真相, 我们首先来点料吧:
图4,图5
bounds.origin
初始情况下为(0,0)
. 我们设置
testView.bounds = CGRectMak(25,25,40,40); //代码5
得到左图. 我们没有改变bounds.size
(仍然是40,40), 只是修改了bounds.origin
: 从(0,0)
改变成(25,25)
.我们发现testView
内部的小红点移动了(0-25,0-25)
距离.(至于为设么这里是-25而不是25, 我也还没理解, 望高人指点.) 反正, 知道当你改变bounds.origin
的时候, testView
内部所有的subView
都要想做相反方向的位移就对了.
在左图的基础上,再设置
testView.bounds = CGRectMak(-5,-5,40,40); //代码6
我们得到右图.
需要注意的是, 这里所有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
):
//代码7
-(CGRect) frame
{
float x = center.x - layer.anchorPoint.x * bounds.width;
float y = center.y - layer.anchorPoint.y * bounds.height;
float width = bounds.width;
float height = bounds.height;
return CGRectMake(x, y, width, height);
}
-(void) setFrame:(CGRect) rect
{
center.x = rect.x + layer.anchorPoint.x * rect.width;
center.y = rect.y + layer.anchorPoint.y * rect.height;
bounds.width = rect.width;
bounds.height = rect.height;
}
因为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来看一个转化:
testView.transform = CGAffineTransformMakeScale(2, 2);//代码8
视图如下:
图6
我们看到,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
). 看看会发现什么:
//代码9
testView.transform = CGAffineTransformMakeScale(1, 1);
testView.layer.anchorPoint = CGPointMake(0,0)
testView.transform = CGAffineTransformMakeScale(2, 2);
下面三个图分别对应上面三行代码执行后的状态:
图7,图8,图9
对于上面的运行结果, 我有几点说明:
- 当我们改变
anchorPoint
的时候,center
没有改变,那么根据代码7,frame
也会随着发生改变. 您可以在图8中观察到这一变化. - 观察图8和图9的变化,你可以发现,这次缩放的中心点在左上角. 因为我们设置了
anchorPoint = CGPointMake(0,0)
. - 如果您是对图像做旋转,
anchorPoint
也是旋转的中心.
到此为止, 文章开始提到的属性都基本讲完了. 只剩下了layer.position
. 如果你细心, 你会发现上面所有的图片中, layer.position === center
, 没错, 任何时候他们都是相等的.
结尾
这里有博客中Demo的代码下载, 本博客中的所有截图都来自于Demo的截屏.如果我在博客中没有说明白, 您可以下载Demo仔细把玩. 相信你可以在实际操作中有更深刻的理解
Demo底部的输入框支持的的语法有:
scale = number
center = ( number, number )
frame = (number, number, number, number )
bounds = (number, number, number, number)
center = (number, number)
position = (number, number) //layer.position
anchorPoint = (number, number) //layer.anchorPoint
比如您输入frame =(0,0,40,40)
,就相当于执行代码testView.frame = CGRectMake(0,0,40,40)
. 输入center = (50,50)
就相当于执行代码testView.center = CGPointMake(50,50)
.
UIView.frame的骗局的更多相关文章
- UIView frame, bounds and center
http://stackoverflow.com/questions/5361369/uiview-frame-bounds-and-center Since the question I asked ...
- UIView 中 frame, bounds, center 属性的关系
最近一直在学 iOS 开发,所以专门创建了这样一个类别,将自己学习中的一些问题整理,记录下来.由于自己是初学者,所以所写的文章非常基础,写这个类别一是为了给自己留下存 档,二是为了给和我有同样问题的初 ...
- 让结构体类型frame的某个属性可以直接修改
本篇是是本人在博客园写的第一篇博客,前几天因为种种原因最终决定离开混了几年的csdn.希望在博客园有个新的开始 Foundation框架里面的frame是大家最熟悉不过的一个属性了,但是修改起来比较麻 ...
- 【原】iOS:一种直接修改frame的某个属性的方法
在iOS中view的frame属性使用地太频繁了,尤其是调UI的时候.我们知道,正常情况下我们无法对frame的某个属性(x,y,width,height等)进行单独修改,比如: someView.f ...
- ios 中直接修改frame里边某个属性的简便方法
参考:http://www.cnblogs.com/wengzilin/p/4359865.html 在iOS中view的frame属性使用地太频繁了,尤其是调UI的时候.我们知道,正常情况下我们无法 ...
- Swift - UIView的无损截图
Swift - UIView的无损截图 效果 源码 // // UIView+ScreensShot.swift // Swift-Animations // // Created by YouXia ...
- iOS - UIView
前言 NS_CLASS_AVAILABLE_IOS(2_0) @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppeara ...
- iOS开发——动画编程Swift篇&(二)UIView转场动画
UIView转场动画 // MARK: - UIView动画-过度动画 var redView:UIView? var blueView:UIView? // enum UIViewAnimation ...
- iOS - UIView操作(SWift)
1. UIView 视图的渐变填充 override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after ...
随机推荐
- Unsupervised Classification - Sprawl Classification Algorithm
Idea Points (data) in same cluster are near each others, or are connected by each others. So: For a ...
- IT外包行业与职业发展
在IT行业,总是有一些IT外包公司的存在,凡是存在的都是合理的.当你做为IT从业人员应该尽量避免去外包公司工作 .特别是你从事软件开发工作. 先来说说缘由,一些外包公司本来是从事软 ...
- 【译】Dependency Injection with Autofac
先说下为什么翻译这篇文章,既定的方向是架构,然后为了学习架构就去学习一些架构模式.设计思想. 突然有一天发现依赖注入这种技能.为了使得架构可测试.易维护.可扩展,需要架构设计为松耦合类型,简单的说也就 ...
- SharePoint List来做项目管理
其实这是一个常见的问题,已经不仅仅只是一次用SharePoint List来做项目管理了. 核心 1. SharePoint List Lookup自己来实现项目的父子关系 2. 权限控制,直接控制在 ...
- Python开发包推荐系列之xml、html解析器PyQuery
使用python,喜欢她的简洁是一方面,另外就是它有着丰富的开发包 好用又方便 接下来会给大家推荐一系列很赞的开发包. 在解析html.xml过程中,我们有不少的包可以用.比如bs.lxml.xmlt ...
- 高精度练习(hdoj1042)
Problem Description Given an integer N(0 ≤ N ≤ 10000), your task is to calculate N! Input One N in ...
- E/AndroidRuntime(1636): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.***.app.wx.MainActivity} : android.view.InflateException: Binary XML file line #51 :
类中加载的xml中,所自定义组件的包名错误(xml中51行错误:自定义组件包名写错了).
- 弃用的异步get和post方法之Block方法
#import "ViewController.h" #import "Header.h" @interface ViewController () <N ...
- iOS 自定义进度条
自定义条形进度条(iOS) ViewController.m文件 #import "ViewController.h" @interface ViewController () @ ...
- iOS多线程-05-多图下载
效果图 常见问题及解决方法 图片重复下载 将内存保存在内存或沙盒中. 若下载的图片量较大,则会出现UI界面不流畅的现象 在子线程中执行下载操作,然后回到主线程成中进行UI界面的刷新. 由于cell的循 ...