在iOS的UI开发中,frame和bounds是两个非常容易搞混的概念,而很多开发者在实际项目中也很少去区分,因此会导致出现一些意想不到的问题。本篇博客以实际代码的方式来学习frame和bounds的使用。相关示例代码上传至 https://github.com/chenyufeng1991/FrameAndBounds ,欢迎大家下载查看。

(1)先来查看一个界面中的容器self.view的frame和bounds的打印结果:下面的运行结果都在iPhone5s模拟器下进行。

[objc] view plain copy  
  1. NSLog(@"self.view.frame = %@",NSStringFromCGRect(self.view.frame));
  2. NSLog(@"self.view.bounds = %@",NSStringFromCGRect(self.view.bounds));

在这里我们可以看到,self.view的frame和bounds是一样的。原点都是在左上角。长宽正好是整个屏幕的宽高。

(2)frame和bounds难道都是一样的吗?当然不是。现在我们对一个View做一个旋转动画。

[objc] view plain copy  
  1. UIView *view01 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 50, 50)];
  2. view01.backgroundColor = [UIColor grayColor];
  3. [self.view addSubview:view01];
  4. NSLog(@"view01.frame = %@",NSStringFromCGRect(view01.frame));
  5. NSLog(@"view01.bounds = %@",NSStringFromCGRect(view01.bounds));
  6. [UIView transitionWithView:view01 duration:2 options:0 animations:^{
  7. view01.transform = CGAffineTransformMakeRotation(M_PI_4);
  8. } completion:^(BOOL finished) {
  9. if (finished)
  10. {
  11. NSLog(@"view01.frame = %@",NSStringFromCGRect(view01.frame));
  12. NSLog(@"view01.bounds = %@",NSStringFromCGRect(view01.bounds));
  13. }
  14. }];

动画效果如下:

我们让一个正方形旋转90度,在动画开始前和结束后分别打印frame和bounds,打印结果如下:

此时可以看到,在动画开始前,frame和bounds也变得不一样了。在旋转动画后,frame发生改变,bounds依然没变。我现在告诉大家下面的结论:

-- frame的位置是根据父容器来计算的,正方形在动画开始前的x=100,y=100是相对于self.view的坐标系统而言的,从而确定当前视图在父视图中的位置。

-- bounds的x,y是根据自己的坐标系统而言的。没错,每个view都有自己的坐标系。以自己左上角点为坐标原点。所以bounds的x,y默认为(0,0),除非调用setBounds方法;

-- frame的size不一定等于bounds的size,在旋转后它们的size就不一样了。

在旋转前后,frame发生了较大的变化,其实旋转后的frame变成了如下:

旋转后:

旋转前:

旋转后,左上角origin的x,y发生了改变,size的height,width也 发生了改变,所以frame也就改变了。我们可以把frame理解为所占区域,其实旋转后的占用区域是发生改变的。但是为什么bounds没有改变呢?

View在旋转过程中,其实自己的坐标系统并没有发生改变,bounds中的origin只能通过setBounds方法修改。 根据英文中的意思,bounds就是边界的意思,在旋转过程中,View的边界长短并没有发生改变,所以bounds的size也就没有改变。

所以我做个小小的别名:把frame理解为占用区域,把bounds理解为边界。

(3)我们把一个子View放到父View中,并且改变父View的bounds,来看看会发生什么?

[objc] view plain copy  
  1. UIView *view02 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
  2. view02.backgroundColor = [UIColor colorWithWhite:0.851 alpha:1.000];
  3. [self.view addSubview:view02];
  4. UIView *view02_sub = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
  5. view02_sub.backgroundColor = [UIColor colorWithRed:1.000 green:0.908 blue:0.552 alpha:1.000];
  6. [view02 addSubview:view02_sub];
  7. [self printFrameAndBounds:view02 viewOfSub:view02_sub];
  8. [UIView animateWithDuration:1 animations:^{
  9. // setBounds 强制将自己坐标系的左上角点改为(-30,-30)。那么真正的原点(0,0)自然向右下角偏移(30,30);
  10. // 注意:setBounds中的(x,y)只改变自己的坐标系统。子view的bounds和frame并不会改变。
  11. [view02 setBounds:CGRectMake(-30, -30, 200, 200)];
  12. } completion:^(BOOL finished) {
  13. [self printFrameAndBounds:view02 viewOfSub:view02_sub];
  14. }];

打印结果如下:

运行效果图如下:

我们通过setBounds把父View的origin=(0,0)改为了(-30,-30), 发现子View向右下方发生了移动。我们来分析一下原因。

我们刚刚提到,setBounds可以改变自己View的坐标系,父View强制把自己左上角的原点(0,0)坐标改为了(-30,-30),但是对于子View.frame.origin=(0,0)来说,它的x,y没有发生改变,仍然是(0,0),   由于左上角已经改为(-30,-30),所以真正的原点(0,0)向右下方移动,所以子View也就向右下方移动了。

做一个小小的总结:

-- setBounds中的(x,y)只改变自己的坐标系统,子View的bounds和frame并不会改变;

-- setBounds是修改自己坐标系的原点位置,进而影响到子View的显示位置;

-- bounds改变位置时,改变的是子视图的位置,自身没有影响,其实就是改变了自身的坐标系原点,默认原点在左上角。

(4)当父View的frame改变的时候,子View会发生什么变化?

[objc] view plain copy  
  1. UIView *view02 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
  2. view02.backgroundColor = [UIColor colorWithWhite:0.851 alpha:1.000];
  3. [self.view addSubview:view02];
  4. UIView *view02_sub = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
  5. view02_sub.backgroundColor = [UIColor colorWithRed:1.000 green:0.908 blue:0.552 alpha:1.000];
  6. [view02 addSubview:view02_sub];
  7. [self printFrameAndBounds:view02 viewOfSub:view02_sub];
  8. [UIView animateWithDuration:1 animations:^{
  9. [view02 setFrame:CGRectMake(0, 250, 150, 150)];
  10. } completion:^(BOOL finished) {
  11. [self printFrameAndBounds:view02 viewOfSub:view02_sub];
  12. }];

打印结果如下:

运行效果动画:

从效果图上可以看到,我们改变了父View的位置和大小(坐标系原点仍然是(0,0),但是实际位置已经改变了,坐标系改变),子View的位置也改变了。但是子View的frame和bounds并没有改变。因为子View.origin是相对于父View的而言的,这并没有改变。

(5)我们上述都只改变了bounds的位置,而没有改变bounds的大小,我们来看看改变bounds的大小会发生什么?

[objc] view plain copy  
  1. UIView *view02 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
  2. view02.backgroundColor = [UIColor colorWithWhite:0.851 alpha:1.000];
  3. [self.view addSubview:view02];
  4. UIView *view02_sub = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
  5. view02_sub.backgroundColor = [UIColor colorWithRed:1.000 green:0.908 blue:0.552 alpha:1.000];
  6. [view02 addSubview:view02_sub];
  7. [self printFrameAndBounds:view02 viewOfSub:view02_sub];
  8. [UIView animateWithDuration:1 animations:^{
  9. [view02 setBounds:CGRectMake(0, 0, 200, 200)];
  10. } completion:^(BOOL finished) {
  11. [self printFrameAndBounds:view02 viewOfSub:view02_sub];
  12. }];

打印结果如下:

运行动画效果如下:

我们使用setBounds方法增大了父View的bounds.size ,可以看到把frame也改变了。所以我做一个小小的总结:

-- 更改bounds的大小,bounds的大小代表当前视图的长和宽,修改长宽后,中心点继续保持不变,长宽进行改变,通过bounds修改长宽就像是以中心点为基准点对长宽两边同时进行缩放。

-- center是根据父容器的相对位置来计算的,无论是修改父容器的bounds还是自身的bounds,都不会改变center。况且使用bounds来缩放view,都是根据center中心点来缩放的,所以center不会改变。

-- setBounds也可以修改view的大小,进而修改frame。

(6)除了有setBounds方法,同样有setFrame方法,我们来看看使用setFrame改变View的大小会发生什么?

[objc] view plain copy  
  1. UIView *viewFather = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)];
  2. viewFather.backgroundColor = [UIColor colorWithWhite:0.741 alpha:1.000];
  3. [self.view addSubview:viewFather];
  4. UIView *viewSub = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 50, 80)];
  5. viewSub.backgroundColor = [UIColor colorWithRed:1.000 green:0.999 blue:0.721 alpha:1.000];
  6. [viewFather addSubview:viewSub];
[objc] view plain copy  
  1. // (6)修改父视图的frame的大小。父容器的center改变,子视图的center不变。
  2. [self printFrameAndBounds:viewFather viewOfSub:viewSub];
  3. [UIView animateWithDuration:3 animations:^{
  4. [viewFather setFrame:CGRectMake(50, 50, 250, 250)];
  5. } completion:^(BOOL finished) {
  6. [self printFrameAndBounds:viewFather viewOfSub:viewSub];
  7. }];

打印结果如下:

动画效果如下:

仔细观察可以看到,setFrame改变大小和setBounds改变大小是完全不一样的,setFrame改变长宽是从左上角原点进行缩放的,固定的是原点。而setBounds则固定的是center。

一句话说:使用frame改变view大小,center改变,因为缩放参考点为左上角。使用bounds改变view大小,center不变,因为缩放参考点为center。

(7)为了上面打印frame,bounds,centre的方便,上面涉及打印父子View方法调用的printFrameAndBounds方法实现如下:

[objc] view plain copy  
  1. - (void)printFrameAndBounds:(UIView *)viewOfFather viewOfSub:(UIView *)viewOfSub
  2. {
  3. NSLog(@"viewOfFather.frame = %@;viewOfFather.bounds = %@;viewOfFather.center = %@",NSStringFromCGRect(viewOfFather.frame),NSStringFromCGRect(viewOfFather.bounds),NSStringFromCGPoint(viewOfFather.center));
  4. NSLog(@"viewOfSub.frame = %@;viewOfSub.bounds = %@;viewOfSub.center = %@",NSStringFromCGRect(viewOfSub.frame),NSStringFromCGRect(viewOfSub.bounds),NSStringFromCGPoint(viewOfSub.center));
  5. }

个人总结了一下:

-- 如果想修改view的位置而不影响其他,修改自身frame的位置;想修改view的大小,修改frame的大小或者bounds的大小(考虑相对位置的改变)。

-- 如果想修改view的所有子view的位置,修改view的bounds的位置(父容器坐标系)。

个人建议,想要查看某个变量的改变效果,可以使用我们初高中实验中的“单一变量原则”,也就是一次代码中只改变一个变量来运行,这样就能很方便的知道某个变量的作用了。

iOS frame与bounds区别详解相关推荐

  1. iOS 证书与签名 解惑详解

    iOS 证书与签名 解惑详解 分类: iPhone2012-06-06 19:57 9426人阅读 评论(1) 收藏 举报 iosxcodecryptographyappleiphone测试 目录(? ...

  2. utf8和utf8mb4的区别详解

    utf8和utf8mb4的区别 一.简介 MySQL在5.5.3之后增加了这个utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode.utf8mb4是utf ...

  3. CSS 中px、em、rem、%、vw、vh单位之间的区别详解【全网最全】

    一.px(像素) [绝对单位,页面按精确像素展示] px就是pixel(像素)的缩写,相对长度单位,相对于屏幕分辨率. px表示像素 (计算机屏幕上的一个点:1px = 1/96in),是绝对单位,不 ...

  4. iOS富文本字符串AttributedString详解

    原文链接: iOS富文本字符串AttributedString详解 简书主页:http://www.jianshu.com/users/37f2920f6848 Github主页:https://gi ...

  5. HashTable和HashMap的区别详解

    HashTable和HashMap的区别详解 一.HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同 ...

  6. java mod %区别_Java中 % 与Math.floorMod() 区别详解

    %为取余(rem),Math.floorMod()为取模(mod) 取余取模有什么区别呢? 对于整型数a,b来说,取模运算或者取余运算的方法都是: 1.求 整数商: c = a/b; 2.计算模或者余 ...

  7. 关于numpy中eye和identity的区别详解

    https://www.jb51.net/article/175386.htm np.identity(n, dtype=None) np.eye(N, M=None, k=0, dtype=< ...

  8. php为什么需要配置路由器,laravel 配置路由 api和web定义的路由的区别详解

    1.路由经过中间件方面不同 打开kerenl.php就可以看到区别 protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware ...

  9. python协程详解_对Python协程之异步同步的区别详解

    一下代码通过协程.多线程.多进程的方式,运行代码展示异步与同步的区别. import gevent import threading import multiprocessing # 这里展示同步和异 ...

  10. Jar/War/Ear等包的作用与区别详解

    Jar/War/Ear等包的作用与区别详解 以客户角度来看,jar文件就是一种封装格式,用户不需要知道jar包中有多少个.class格式的文件及每个文件中的功能与作用,也可以得到相应的访问的结果.ja ...

最新文章

  1. 公司的API接口被刷了,那是因为你没这样做
  2. 【转载】Eclipse 最常用快捷键 (动画讲解),最简单的一些快捷键
  3. MFC对话框退出程序所调用的函数
  4. C++ WINDOWS API 如何使用NMAKE和CL编译
  5. 【转】SAP LIST 画框的FORM
  6. 为什么有些xpath绝对路径拿不到数据_Python爬虫,登陆神器Selenium之xpath的使用
  7. java脚本接口自动化测试_接口自动化测试实践的记录
  8. 工控蜜罐Conpot部署和入门及高级演变
  9. webStorm编辑器(左侧目录)如何显示、隐藏?
  10. C语言 mallocfree
  11. 通过线程监控socket服务器是否done机
  12. ArcGIS学习总结(11)——创建点要素并计算对应经纬度
  13. .Net中数据绑定控件应用小技巧
  14. Ehcache 的简单使用
  15. 有意思的六度分割理论
  16. web前端知识——iframe标签、CSS
  17. win10繁体字改简体字方法
  18. 预测大盘最准确的指标_预测大盘走势最牛的指标(经典)
  19. osgEarthSamples1.1运行事例
  20. 网络入侵检测--Snort软件规则编写

热门文章

  1. JS代码计算正方教务系统目前成绩的加权平均分和平均绩点
  2. ipad、iphone 怎么真机调试
  3. 2022年玻璃包装容器行业市场发展环境分析预测及下游需求规模增长率研究预测
  4. 为你的掘金和 GitHub 设定个性域名
  5. 百度网盘四种方法免费提速
  6. jwt怎么获取当前登录用户_获取jwt(json web token)中存储的用户信息
  7. canvas节点无法导出图片_HTML转为PDF,图片导出失败的终极解决方案
  8. 被中二病拯救 ——看《中二病也要谈恋爱》第一季
  9. 乌镇现场 | 倪光南:人工智能应该更好的为人类的服务
  10. Wps ppt中无法打开超链接外部文件的解决办法。