布局/定位相关 @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,但是frame为0时也不会触发 3>设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化 4>改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件 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:的用法验证
打印结果
//sizeToFit的用法验证
打印结果