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对subviews重新布局

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方法的应用:

1. NSLayoutConstraint动画 需要在 对NSLayoutConstraint的对象赋值之后调用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 - 布局重绘机制相关方法的研究的更多相关文章

  1. iOS之 重绘机制

    最近在看Core Animation , 今天来谈谈CALayer 和 UIView 中的重绘的一些认识: 我们都知道UIView里面有个成员layer,利用这个这个layer我们可以设置一些圆角,阴 ...

  2. android控件拖动,移动、解决父布局重绘时控件回到原点

    这是主要代码: 保证其params发生改变,相对于父布局的位置就能达到位置移动到原来的位置 // 每次移动都要设置其layout,不然由于父布局可能嵌套listview,当父布局发生改变冲毁(如下拉刷 ...

  3. Phone重绘机制drawRect 转

    Phone重绘机制drawRect 如何使用iPhone进行绘图.重绘操作iPhone的绘图操作是在UIView类的drawRect方法中完成的,所以如果我们要想在一个UIView中绘图,需要写一个扩 ...

  4. iOS重绘机制drawRect

    iOS的绘图操作是在UIView类的drawRect方法中完成的,所以如果我们要想在一个UIView中绘图,需要写一个扩展UIView 的类,并重写drawRect方法,在这里进行绘图操作,程序会自动 ...

  5. iPhone重绘机制drawRect

    如何使用iPhone进行绘图.重绘操作iPhone的绘图操作是在UIView类的drawRect方法中完成的,所以如果我们要想在一个UIView中绘图,需要写一个扩展UIView 的类,并重写draw ...

  6. Qt重绘机制

    一.引发重绘的事件 1.调用repaint() 2.调用uodate() 二.控件hide或者show 三.其他 ps: repaint函数是立即重绘,没有优化 update会优化,异步重绘,所以如果 ...

  7. iOS之UI--Quartz2D的入门应用--重绘下载圆形进度条

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  8. 浏览器渲染详细过程:重绘、重排和 composite 只是冰山一角

    https://juejin.im/entry/590801780ce46300617c89b8 渲染 这张很经典的图许多人都看过,其中的概念大家应该都很熟悉,也就是这么几个步骤:js修改dom结构或 ...

  9. 【Android】利用自己定义View的重绘实现拖动移动,获取组件的尺寸

    以下利用一个app来说明怎样利用自己定义View的重绘实现拖动移动.获取组件的尺寸. 例如以下图,触摸拖动,或者轻轻点击屏幕都能移动图片.假设碰到文字,则会弹出提示. 这里是利用自己定义View的重绘 ...

随机推荐

  1. 纯CSS3实现漂亮的价格表样式代码

    分享一款纯CSS3实现漂亮的价格表样式代码是一款常见的主机商发布产品价格信息页.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div id="main" ...

  2. python idea 利用树莓派做家庭报警系统

    1 利用树莓派做家庭报警系统idea 功能如下: 1.程序家侧人不在家(7:00-6:00) 2.树莓派搭配摄像头,对这门进行图像识别,如果变化,门开了,就报警: 3.报警的方式是给我发短信,采信,或 ...

  3. ExtJS获取Grid的行数

    1.     grid.getSelectionModel().getCount() ;  // 获得当前选中的行数  2.     grid.getStore().getTotalCount();  ...

  4. 内网环境NTP服务及时间同步(CentOS6.x)配置和部署

    目标环境,5台linux centos 6.3, 一台作为NTPD服务与外部公共NTP服务同步时间,同时作为内网的NTPD服务器,其他机器与这台服务做时间同步.  服务器IP 角色   说明 同步方式 ...

  5. SparkR:数据科学家的新利器

    摘要:R是数据科学家中最流行的编程语言和环境之一,在Spark中加入对R的支持是社区中较受关注的话题.作为增强Spark对数据科学家群体吸引力的最新举措,最近发布的Spark 1.4版本在现有的Sca ...

  6. sparkr基本操作1

    由于装的sparkr是1.4版本的,老版本的很多函数已经不再适用了. 在2台服务器的组成的集群中测试了一版数据,熟悉下这个api的基本操作.​ libpath <- .libPaths() li ...

  7. array、isset、三元运算符、find()

    array('name','getName',3,'callback'), // 对name字段在新增和编辑的时候回调getName方法 if(isset($_GET['id'])) { // 根据i ...

  8. C++ 类的隐式转换

    所谓类的隐式转换,就是将实参类型转成形参类型--如果不一致的话. 这个转换与基本类型转换不太一样,具体则是在形参类型的构造函数中使用实参类型的数据,从而构造出一个临时对象. 下面的代码,类Person ...

  9. free 和delete 把指针怎么啦?

    别看 free 和 delete 的名字恶狠狠的(尤其是 delete),它们只是把指针所指的内存给 释放掉,但并没有把指针本身干掉. 发现指针 p 被 free 以后其地址仍然不变(非 NULL), ...

  10. Binding to a Service

    应用组件(客户端)可以通过 bindService()方法绑定到service,Android系统随后会调用service的 onBind()方法,返回一个 IBinder 用于和service交互. ...