layoutSubviews 与 autoresizingMask

在ios的开发中,遇到UIView的排版问题,自然少不了layoutSubviews 这个函数与autoresizingMask这个属性。

在superview的autoresizesSubviews为Yes的时候,会根据subview的autoresizingMask类型进行自动排版,autoresizingMask可选的属性有

UIViewAutoresizingNone                 = 0,

UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,

UIViewAutoresizingFlexibleWidth        = 1 << 1,

UIViewAutoresizingFlexibleRightMargin  = 1 << 2,    

UIViewAutoresizingFlexibleTopMargin    = 1 << 3,    

UIViewAutoresizingFlexibleHeight       = 1 << 4,    

UIViewAutoresizingFlexibleBottomMargin = 1 << 5

 

因为横向和纵向的变换方式是一样的,所以就以iPhone中更常用的纵向变换为例了:

UIViewAutoresizingNone:superview变换时,自己不作变换。

UIViewAutoresizingFlexibleHeight:上边距不变,和superview在高度上变换同等高度。 比如,superview加高100,则自己也加高100。

UIViewAutoresizingFlexibleTopMargin:高度不变。上边距弹性可变,下边距保持不变。

UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleHeight: 这个组合的变换比较绕: 首先,下边距是不变的,但高和上边距会变,变换的计算如下, 比如superview的高度,由100加高的200。自己的下边距是50, 则去掉不变的下边距后,superview的变化比例是:(100-50)/(200-50) = 50/150 = 1/3。 则自己的上边距和高都变为越来的3倍。

UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin : 这个变换的计算就比较简单了,救是自己的上边距,高,下边距都和superview同比变换。 比如superview的高由100变为200。则自己的上边距,高,下边距也都变为原来的2倍。

但对layoutSubviews这个函数何时会被调用却一直不是很清楚,只是知道设置frame的时候,会异步的调用,这里的文章中总结出了几点场景:

  • init does not cause layoutSubviews to be called (duh)
  • addSubview causes layoutSubviews to be called on the view being added, the view it’s being added to (target view), and all the subviews of the target view
  • setFrame intelligently calls layoutSubviews on the view having it’s frame set only if the size parameter of the frame is different
  • scrolling a UIScrollView causes layoutSubviews to be called on the scrollView, and it’s superview
  • rotating a device only calls layoutSubview on the parent view (the responding viewControllers primary view)
  • removeFromSuperview – layoutSubviews is called on superview only (not show in table)

针对setFrame这一条,曾经以为在layoutSubviews中通过self.frame重新设置一个不同的frme,会再次调用layoutSubviews从而导致死循环,经过实验发现并未产生,还不知道具体的原因。

在如果subview设置了autoresizingMask,而supview中的重写了layoutsubviews,并且其中对subview进行了指定排版,那么subview的autoresizingMask将不会起作用的

 

When does layoutSubviews get called?

http://blog.logichigh.com/2011/03/16/when-does-layoutsubviews-get-called/

It’s important to optimize any UIView layoutSubviews method you create, as it can be frequently called, and has the potential for creating recursion (triggering a setNeedsLayout from layoutSubviews can create a loop that will grossly affect your apps performance). Layout subviews is called once per run loop on any view that has had setNeedsLayout or setNeedsDisplayWithRect: called on it. So in addition to any time you manually call these methods, it can be useful to know when the UI framework calls setNeedsLayout/setNeedsDisplay as this will trigger layoutSubviews.

For this purpose, I will define a few view relationships:

  • View1 – UIView class, root view for examples
  • View1.1 – UIScrollView class, subview of View1
  • View1.1.1 – UIView class, subview of View1.1 (No autoresize mask)
  • View1.1.2 – UIView class, another subview of View1.1 (Autoresize mask – flexible width)

I then ran the following tests.  An X means the view was layed out

From this I surmise the following:

  • init does not cause layoutSubviews to be called (duh)
  • addSubview causes layoutSubviews to be called on the view being added, the view it’s being added to (target view), and all the subviews of the target view
  • setFrame intelligently calls layoutSubviews on the view having it’s frame set only if the size parameter of the frame is different
  • scrolling a UIScrollView causes layoutSubviews to be called on the scrollView, and it’s superview
  • rotating a device only calls layoutSubview on the parent view (the responding viewControllers primary view)
  • removeFromSuperview – layoutSubviews is called on superview only (not show in table)

Hopefully this is helpful information for you as well.

Share

6 Comments

  • By Dan Wineman, March 29, 2011 @ 9:51 am

    I find that resizing (not moving, only resizing) any subview causes the superview to get -layoutSubviews sent to it, which I found unexpected. From your table it doesn’t look like you tested that case.

  • By Robb Albright, September 21, 2011 @ 12:35 pm

    I’m finding the same thing as Dan: If you resize view 1.1, then view1 will get layoutsubviews called on it.

    Surprising when you are using layoutsubviews to handle complex arrangement of subviews for orientation changes, but also want to move the subviews in response to user action.

  • By Juguang XIAO, September 25, 2011 @ 10:22 pm

    For device rotation, as you stated:

    - rotating a device only calls layoutSubview on the parent view (the responding viewControllers primary view)

    This is partially true. This can be true only when your VC is in the VC hierarchy (root at window.rootViewController), well this is most common case. In iOS 5, if you create a VC, but it is not added into any another VC, then this VC would not get any noticed when device rotate, therefore its view would not get noticed too by calling layoutSubviews

posted @ 2013-04-18 11:43  major1314  阅读(576)  评论(0)    收藏  举报