iOS - 布局重绘机制相关方法的研究
iOS View布局重绘机制相关方法
布局
- (void)layoutSubviews
- (void)layoutIfNeeded
- (void)setNeedsLayout
——————————————————————————————
重绘
- (void)drawRect
- (void)setNeedsDisplay
- (void)setNeedsDisplayInRect:(CGRect)invalidRect
——————————————————————————————
- (CGSize)sizeThatFits:(CGSize)size
- (void)sizeToFit
一、布局
刷新子控件的布局
-layoutSubviews方法: 这个方法不能直接调用这个方法 ,由系统调用,默认没有做任何事情,一般进行重写调用super 之后可以在这里设置子控件的frame
-setNeedsLayout方法: 标记上一个需要被重新布局标记,异步调用layoutIfNeeded刷新布局,不立即刷新,但layoutSubviews一定会被调用
-layoutIfNeeded方法:如果有需要刷新的标记,立即调用layoutSubviews进行布局(如果没有标记,不会调用layoutSubviews)
- (void)layoutSubviews;
这个方法不能直接调用这个方法 ,由系统调用,默认没有做任何事情,一般进行重写调用super 之后可以在这里设置子控件的frame
以下情况会触发系统调用 layoutSubviews方法
- 1、init初始化不会触发layoutSubviews
- 2、addSubview会触发layoutSubviews
- 3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
- 4、滚动一个UIScrollView会触发layoutSubviews
- 5、旋转Screen会触发父UIView上的layoutSubviews事件
- 6、改变一个UIView大小的时候也会触发其父类的UIView上的layoutSubviews事件
- 7、直接调用setLayoutSubviews 或者 layoutIfNeeded
- (void)layoutSubviews{
[super layoutSubviews];
//可以在这里设置子控件的frame等 }
layoutSubviews官方讲解
Lays out subviews.
The default implementation of this method does nothing on iOS 5.1 and earlier. Otherwise, the default implementation uses any constraints you have set to determine the size and position of any subviews.
在iOS5.1或之前的版本中,这个方法什么也没干.这个方法的默认实现是用参数来设定subviews的尺寸和位置的.
Subclasses can override this method as needed to perform more precise layout of their subviews. You should override this method only if the autoresizing and constraint-based behaviors of the subviews do not offer the behavior you want. You can use your implementation to set the frame rectangles of your subviews directly.
如果你需要更加精确的布局,可以在子类里面重写这个方法.仅仅在以下情况下:自动布局达不到你想要效果时你才有必要重写这个方法.你可以直接设置subviews的尺寸.
You should not call this method directly. If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded method.
你不能直接调用这个方法.这个方法是系统调用的 如果你需要强制layout刷新,调用setNeedsLayout来代替.如果你想要立即刷新你的view,调用layoutIfNeeded
调用-setNeedsLayout方法:
就是我们主动为这个视图设置一个标记(flag), 标记为需要重新布局,告诉系统这个视图再下一个时机到来时要重新渲染.,异步调用layoutIfNeeded刷新布局,(不立即刷新,因为有标记所以 layoutSubviews一定会被调用)
在receiver标上一个需要被重新布局的标记,在系统runloop的下一个周期自动调用layoutSubviews
调用-layoutIfNeeded方法:
告诉系统,如果设置了flag那么不用等待时机到来了,直接渲染吧 .立即调用layoutSubviews进行布局(如果没有标记,不会调用layoutSubviews)
结合上面的细细品味下面的内容总结:
在我们没有任何干预的情况下,一个view的frame或bounds发生变化时,系统会设置一个flag给这个view,当下一个渲染时机到来时系统会重新按新的布局来渲染视图(系统会调用layoutSubViews)。setNeedLayout就是我们主动为这个视图设置一个flag,告诉系统这个视图再下一个时机到来时要重新渲染,而layoutIfNeed则是告诉系统,如果设置了flag那么不用等待时机到来了,直接渲染吧。而layoutSubviews这个方法是系统调用的,我们不需要主动调用,我们只需要调用layoutIfNeed就可以了,让系统判断是否在当前时机下立即渲染。
⚠️值得注意的是:设置标记的时机
- 1、一个view的frame或bounds发生变化时,系统会设置一个flag给这个view
- 2、主动调用setNeedLayout设置标记
- 3、xib 中的约束值变了系统也会设置flag self.blueViewW.constant = 80; 系统底层会转换为frame 所以会设置一个flag
二、重绘
-drawRect:(CGRect)rect方法:不能直接调用这个方法 ,由系统调用,重写此方法,执行重绘任务
-setNeedsDisplay方法:标上一个需要被重新绘图的标记,异步调用drawRect
-setNeedsDisplayInRect:(CGRect)invalidRect方法:标记为需要局部重绘
- (void)drawRect:(CGRect)rect;
不能直接调用这个方法 ,由系统调用,重写此方法,执行重绘任务
以下情况会触发系统调用 drawRect
方法
- 1、如果在UIView初始化时没有设置rect大小,将直接导致drawRect不被自动调用。drawRect 调用是在Controller->loadView, Controller->viewDidLoad两方法之后掉用的.所以不用担心在控制器中,这些View的drawRect就开始画了.这样可以在控制器中设置一些值给View(如果这些View?draw的时候需要用到某些变量 值).
- 2、该方法在调用sizeToFit后被调用,所以可以先调用sizeToFit计算出size。然后系统自动调用drawRect:方法。
- 3、通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
- 4、直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,但是有个前提条件是rect不能为0。
drawRect方法使用注意点:
- 1、 若使用UIView绘图,只能在drawRect:方法中获取相应的contextRef并绘图。如果在其他方法中获取将获取到一个invalidate 的ref并且不能用于画图。drawRect:方法不能手动显示调用,必须通过调用setNeedsDisplay 或 者 setNeedsDisplayInRect,让系统自动调该方法。
- 2、若使用calayer绘图,只能在drawInContext: 中(类似鱼drawRect)绘制,或者在delegate中的相应方法绘制。同样也是调用setNeedDisplay等间接调用以上方法
- 3、若要实时画图,不能使用gestureRecognizer,只能使用touchbegan等方法来掉用setNeedsDisplay实时刷新屏幕
-setNeedsDisplay
在receiver标上一个需要被重新绘图的标记,在下一个draw周期自动重绘,iphone device的刷新频率是60hz,也就是1/60秒后重绘
-setNeedsDisplayInRect:(CGRect)invalidRect:
标记为需要局部重绘
⚠️注意
layoutSubviews方法调用先于drawRect
setNeedsLayout在receiver标上一个需要被重新布局的标记,在系统runloop的下一个周期自动调用layoutSubviews
layoutIfNeeded方法如其名,UIKit会判断该receiver是否需要layout.根据Apple官方文档,layoutIfNeeded方法应该是这样的
layoutIfNeeded遍历的不是superview链,应该是subviews链
drawRect是对receiver的重绘,能获得context
setNeedDisplay/setNeedsDisplayInRect 在receiver标上一个需要被重新绘图的标记,在下一个draw周期自动重绘,iphone device的刷新频率是60hz,也就是1/60秒后重绘
三
- (void)sizeToFit; //手动调用,不能重写,系统会根据内容的帮我布局一个它认为最合适的 最优的 size 而且会改变自己的size
- (CGSize)sizeThatFits:(CGSize)size; //手动调用,可以在子类中重写,会计算出最优的 size 并返回 不会改变自己的size
- (CGSize)sizeThatFits:(CGSize)size; // return 'best' size to fit given size. does not actually resize view. Default is return existing view size - (void)sizeToFit; // calls sizeThatFits: with current view bounds and changes bounds size.
- (void)sizeToFit
- sizeToFit会自动调用sizeThatFits方法;
- sizeToFit不应该在子类中被重写,应该重写sizeThatFits
- 调用sizeToFit 系统会根据内容的帮我布局一个它认为最合适的 最优的 size 而且会改变当前view自己的size
sizeToFit应用场合
另外sizeToFit声明在UIView中,所以我们的所有的视图控件,都可以调用这个方法,但是实际的开发中,我们好像也不经常使用它,来做一些布局什么的?
原因:我们一般在不方便手动布局的时候才调用sizeToFit方法,以下场合不适合手动布局可以用sizeToFit:
- 1. navigationBar中对navigationItem的设置,(添加两个视图以上的控件到Item)
- 2. toolBar中的对UIBarButtonItem的设置(一般我们还要添加弹簧控件)
上述两种场合就可以用sizeToFit这个方法,来让系统给我们做自动布局。(注意:如果就添加一个控件的话,我们直接设置frame也是可以的) - 3.在tabBar中我们不能手动的添加的子控件,因为tabBar是根据控制器系统默认自动添加的tabBarItem。(猜想系统可能也会自动调用了这个方法)
- 4.UILabel中添加文字,然后让调整label的大小来适应文字,我们也调用sizeToFit的方法。
- (CGSize)sizeThatFits:(CGSize)size;
返回最合适的大小,实际上不调整视图(即不会改变自己的size)。默认值是返回现有的视图大小
以上两个放的应用:使用这两个方法之前,必须要给uilabel赋值,否则不会显示内容的。
UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 50, 0, 0)]; testLabel.backgroundColor = [UIColor whiteColor]; testLabel.text = @"我们都有一个家啊,名字叫中国,家里攀着两条龙"; testLabel.font = [UIFont systemFontOfSize:20]; testLabel.textColor = [UIColor blackColor]; [testLabel sizeThatFits:CGSizeMake(20, 20)];//会计算出最优的 size 并返回 不会改变label 的size NSLog(@"testLabel sizeThatFits frame = %@", NSStringFromCGRect(testLabel.frame)); NSLog(@"best size = %@",NSStringFromCGSize([testLabel sizeThatFits:CGSizeMake(20, 20)])); [testLabel sizeToFit];//会计算出最优的 size 而且会改变自己的size 内部会调用sizeThatFits去计算 NSLog(@"testLabel sizeToFit frame = %@",NSStringFromCGRect(testLabel.frame)); [self.view addSubview:testLabel];
layoutIfNeeded方法的应用:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.blueViewW.constant = 80; //frame /bound 发生改变(系统会对布局进行渲染 但是如果想做动画的话 需要如下自己强制执行渲染) [UIView animateWithDuration:2.0 animations:^{
//对布局进行渲染
[self.view layoutIfNeeded]; //layoutIfNeeded方法只会刷新子控件,因此要使用必须通过它的父类 }]; }
2.tableView cell 的高度的获取
iOS - 布局重绘机制相关方法的研究的更多相关文章
- iOS之 重绘机制
最近在看Core Animation , 今天来谈谈CALayer 和 UIView 中的重绘的一些认识: 我们都知道UIView里面有个成员layer,利用这个这个layer我们可以设置一些圆角,阴 ...
- android控件拖动,移动、解决父布局重绘时控件回到原点
这是主要代码: 保证其params发生改变,相对于父布局的位置就能达到位置移动到原来的位置 // 每次移动都要设置其layout,不然由于父布局可能嵌套listview,当父布局发生改变冲毁(如下拉刷 ...
- Phone重绘机制drawRect 转
Phone重绘机制drawRect 如何使用iPhone进行绘图.重绘操作iPhone的绘图操作是在UIView类的drawRect方法中完成的,所以如果我们要想在一个UIView中绘图,需要写一个扩 ...
- iOS重绘机制drawRect
iOS的绘图操作是在UIView类的drawRect方法中完成的,所以如果我们要想在一个UIView中绘图,需要写一个扩展UIView 的类,并重写drawRect方法,在这里进行绘图操作,程序会自动 ...
- iPhone重绘机制drawRect
如何使用iPhone进行绘图.重绘操作iPhone的绘图操作是在UIView类的drawRect方法中完成的,所以如果我们要想在一个UIView中绘图,需要写一个扩展UIView 的类,并重写draw ...
- Qt重绘机制
一.引发重绘的事件 1.调用repaint() 2.调用uodate() 二.控件hide或者show 三.其他 ps: repaint函数是立即重绘,没有优化 update会优化,异步重绘,所以如果 ...
- iOS之UI--Quartz2D的入门应用--重绘下载圆形进度条
*:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...
- 浏览器渲染详细过程:重绘、重排和 composite 只是冰山一角
https://juejin.im/entry/590801780ce46300617c89b8 渲染 这张很经典的图许多人都看过,其中的概念大家应该都很熟悉,也就是这么几个步骤:js修改dom结构或 ...
- 【Android】利用自己定义View的重绘实现拖动移动,获取组件的尺寸
以下利用一个app来说明怎样利用自己定义View的重绘实现拖动移动.获取组件的尺寸. 例如以下图,触摸拖动,或者轻轻点击屏幕都能移动图片.假设碰到文字,则会弹出提示. 这里是利用自己定义View的重绘 ...
随机推荐
- pku oj overhang叠加卡片求最少的卡片数
这个估计是里面第二简单的了,因为第一简单的是求a+b 哈哈,一submit就ac了 题目如下: Description How far can you make a stack of cards ov ...
- 早前的java笔记
多个if语句时即使前一个if为真,后面的if也要继续判断, 如果用else if 则只有前者为假的时候它的if 才接着判断. if else 语句 if a>=5 shuchu b=10 els ...
- vsftp移植(待续)
1. 下载sftp包,解压.如vsftpd-2.3.5.tar.gz2. 编译 进入目录后,修改交叉编译工具(vi Makefile) CC = arm-none-linux-gnueabi ...
- 400错误,Required String parameter 'paramter' is not present
1.就拿简单的登录来说吧,这是开始的代码 @RequestMapping(value="/login")public ModelAndView login(@RequestPara ...
- subversion commit 报错A checksum mismatch occurred
期望: 2bea3643d1ea67bfa032af9dd649b1d5 实际: 5693bd753ac9ab1ddfcdd9a644b6bd02 在eclipse工程中出现此问题,则 1.com ...
- java分布式集群
http://blog.csdn.net/guzicheng/article/details/11580841
- 实现对DataGird控件的绑定操作
//实现对DataGird控件的绑定操作 function InitGrid(queryData) { $('#grid').datagrid({ //定位到Table标签,Table标签的ID是gr ...
- r语言 技巧总结
1.table函数返回众数,再转为dataframe as.data.frame(table(x)) 2.使用which 返回数组下标 which(rs.list=="rs1008507&q ...
- python中的矩阵、多维数组----numpy
https://docs.scipy.org/doc/numpy-dev/user/quickstart.html (numpy官网一些教程) numpy教程:数组创建 python中的矩阵.多维数 ...
- Maven学习日记(一)----构建web项目
创建maven-web项目: dos进入workspace目录: D:\workspace>mvn archetype:create -DgroupId=org.sonatype.mavenbo ...