layoutSubviews和drawRect:(CGRect)rect的用法探讨

布局/定位相关
@interface UIView(UIViewHierarchy)
- (void)setNeedsLayout; 
注意:
   1.在receiver标上一个需要被重新布局的标记,在系统runloop的下一个周期自动调用layoutSubviews。
- (void)layoutIfNeeded; 
注意:
   1.方法如其名,UIKit会判断该receiver是否需要layout.根据Apple官方文档,layoutIfNeeded方法应该是这样的layoutIfNeeded遍历的不是superview链,
    应该是subviews链。
- (void)layoutSubviews;    
注意:
  1.这是核心函数,最终的目的就是调用该函数,开发者不能直接调用该函数,但可以重写该函数,来加入些自己的代码。
    该函数只会进行位置,视图大小的数字计算,并不会引起屏幕的绘制。
  2.在苹果的官方文档中强调 
    "You should override this method only if the autoresizing behaviors of the subviews do not offer the behavior you want."
   大意是:当我们在某个类的内部调整子视图位置时,需要调用;如果你想要在外部设置subviews的位置,就不要重写。
  3.如果要立即刷新,要先调用[view setNeedsLayout],把标记设为需要布局,然后马上调用[view layoutIfNeeded],实现布局. 
     在视图第一次显示之前,标记总是“需要刷新”的,可以直接调用[view layoutIfNeeded]
  4.layoutSubviews自动调用的时机
    1>init初始化不会触发layoutSubviews,
    2>addSubview会触发layoutSubviews(此时的父类和子类的layoutSubViews都会触发,但是frame为0时子类的不会触发,父类的会触发).
    3>设置view的Frame会触发layoutSubviews(此时的父类和子类的layoutSubViews都会触发,但是frame为0时子类的不会触发,父类的会触发)
    4>改变一个UIView大小的时候也会触发layoutSubviews事件(此时的父类和子类的layoutSubViews都会触发,但是frame为0时子类的不会触发,父类的会触发)
    5>不用滚动就会触发UIScrollView会触发layoutSubviews
    6>旋转Screen会触发父UIView上的layoutSubviews事件
@end

显示相关
@interface UIView(UIViewRendering)
- (void)setNeedsDisplay;
注意:
   1.在receiver标上一个需要被重新绘图的标记,在下一个draw周期自动重绘,iphone device的刷新频率是60hz,也就是1/60秒后重绘;
- (void)setNeedsDisplayInRect:(CGRect)rect;
注意:   
   1.不但设置了flag,而且详细规定了需要刷新的区域。
- (void)drawRect:(CGRect)rect;
注意:
   1.这是核心函数,最终导致显示到屏幕上。开发人员不可以直接调用该函数,只能重写该函数,额外做一些我们想做的事。是对receiver的重绘,能获得context。
   2.在UIView中,重写drawRect: (CGRect) aRect方法,可以自己定义想要画的图案.且此方法一般情况下只会画一次。也就是说这个drawRect方法一般情况下只会被调用一次
   3.调用setNeedsDisplay会自动调用drawRect(注意苹果不建议直接调用drawRect方法),前提是rect大小存在,如果在UIView初始化时没有设置rect大小,将直接导致drawRect不被自动调用。
   4.drawRect调是在Controller->loadView, Controller->viewDidLoad 两方法之后掉用的.所以不用担心在控制器中,这些View的drawRect就开始画了
   5.该方法在调用sizeThatFits后被调用,所以可以先调用sizeToFit计算出size。然后系统自动调用drawRect:方法。
   6.通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
   7.若使用UIView绘图,只能在drawRect:方法中获取相应的contextRef并绘图。如果在其他方法中获取将获取到一个invalidate的ref并且不能用于画图。drawRect:方法不能手动显示调用,必须通过调用setNeedsDisplay 或者 setNeedsDisplayInRect ,让系统自动调该方法。
   8.若使用calayer绘图,只能在drawInContext: 中(类似鱼drawRect)绘制,或者在delegate中的相应方法绘制。同样也是调用setNeedDisplay等间接调用以上方法。
   9.若要实时画图,不能使用gestureRecognizer,只能使用touchbegan等方法来掉用setNeedsDisplay实时刷新屏幕
  10.调用setNeedsLayout和layoutIfNeeded 也会自动调用drawRect,前提是rect大小存在,如果在UIView初始化时没有设置rect大小,将直接导致drawRect不被自动调用。
@end

@interface UIView(UIViewGeometry)
- (CGSize)sizeThatFits:(CGSize)size;  // return 'best' size to fit given size. does not actually resize view. Default is return existing view size
注意: 1.会计算出最优的 size 但是不会改变自己的size (要想改变label的size ,还要把计算的结果,赋给相应的view);
         2.参数Size 你可以输入任何值,不影响
- (void)sizeToFit;                                  // calls sizeThatFits: with current view bounds and changes bounds size.
注意: 1.会计算出最优的 size 而且会改变自己的size(view直接就改变自身的大小)
@end

示例:

#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    UILabel * label =[[UILabel alloc]initWithFrame:CGRectMake(100, 100, 100, 20)];
    label.backgroundColor =[UIColor blueColor];
    label.text =@"sizeThatFits: 和 sizeToFit 验证";
    label.font=[UIFont systemFontOfSize:20];
    label.textColor =[UIColor blackColor];
    NSInteger i =1;// 1或者2
    switch (i){
        case 1:
            [self  sizeThatFitsSender:label];
            break;
        case 2:
            [self sizeToFitSender:label];
        default:
            break;
    }
    [self.view addSubview:label];
}
//sizeThatFits:的用法验证
-(void)sizeThatFitsSender:(UILabel *)label{
    [label sizeThatFits:CGSizeMake(20, 20)];//会计算出最优的size 但是不会改变自己的size.
    NSLog(@"label sizeThatFits frame=%@ ",NSStringFromCGRect(label.frame));
    NSLog(@"best size = %@ ",NSStringFromCGSize([label sizeThatFits:CGSizeMake(2, 1)]));
}
//sizeToFit的用法验证
-(void)sizeToFitSender:(UILabel *)label{
      [label sizeToFit];//会计算出最优的 size 而且会改变自己的size
      NSLog(@"testLabel sizeToFit frame = %@",NSStringFromCGRect(label.frame));
}
//sizeThatFits:的用法验证 

打印结果



layoutSubviews和drawRect:(CGRect)rect的用法探讨_第1张图片

//sizeToFit的用法验证

打印结果



layoutSubviews和drawRect:(CGRect)rect的用法探讨_第2张图片















你可能感兴趣的:(layoutSubviews和drawRect:(CGRect)rect的用法探讨)