美文网首页
控制器加载过程及UI初始化

控制器加载过程及UI初始化

作者: 简_爱SimpleLove | 来源:发表于2017-03-20 17:35 被阅读146次

控制器加载过程

项目过程中,控制器可以看作是UIView的管理者,而且UIView可以看作是CALayer的管理者
一、控制器 初始化方法(是否有xib)
代码和xib的区别(普通代码构建,直接使用代码CODE而不用翻译XIB中的数据(xml),省了一个步骤)
1. init 和 initWithNibName
init底层执行了initWithNibName(默认加载)
首先去找有没有相关的xib文件,没有就执行loadview创建view, 有就不创建
2. loadview方法 (底层实现)
注意:xib的情况下,在viewDidLoad里面加载的view还是xib上大小的view,执行完才会适配(两种autolayout, autosize)
self.view 如果view 为空,那么self.view的get方法,会进入一个死循环

  • -(instancetype)init;
  • -(instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
  • -(void)loadView;
  • -(void)viewDidLoad;
  • -(void)viewWillAppear:(BOOL)animated;
  • -(void)viewDidAppear:(BOOL)animated;

注:

  1. init 封装了initWithNibName方法
  2. 创建类的时候使用eocViewCtl = [[EOCViewContrl alloc] initWithNibName:nil bundle:nil]代替 eocViewCtl = [[EOCViewContrl alloc] init];可以跳过init方法中的一些逻辑而直接通过initWithNibName方法进行创建。
    initWithNibName方法中name和bundle都传nil时,name会默认找与类名相同的nib文件,而bundle则是默认在mainBundle中去寻找。
  3. 如果是通过xib文件创建的控制器,则最好不要重写loadView方法,因为只要你重写,就会走这个方法,也就会覆盖掉你在xib中进行的操作。如果重写了loadView方法过后,想要加载Xib,就必须指定xib文件。

SecondViewCtr *vctr = [[SecondViewCtr alloc] initWithNibName:@"SecondViewCtr" bundle:nil];

  1. 就算我们不重写loadView,系统也肯定会走,只是当它发现有xib的时候,他就通过xib来进行创建,如果没有就通过别的方式进行创建。
  2. self.view 是懒加载的模式,懒加载在view的get方法中执行了loadview和viewDidLoad,如果你重写loadView,并且在viewDidLoad中又用到self.view,则会形成循环,无限走loadView和viewDidLoad,直至崩溃。类似于这样的代码。
    if (!_view) {
        [self loadView];
        [self viewDidLoad];
    }
return _view;
  1. 当我们需要进行项目拆分的时候,可以重写loadView方法,在里面将控制器的View替换成我们自定义的View,从而在View中实现一些业务逻辑。

UI初始化

二、UI的初始化
代码初始化:
init initWithFrame init封装了initWithFrame
xib初始化:
initWithCoder awakeFromNib

layoutSubviews 什么时候掉用(没有superview是不会掉用,除第一条例外)
1 第一次addsubview(加载到界面)的时候,提前条件有frame
2 修改本身frame的时候(前提是frame的值前后发生了变化)
3 修改子视图的frame的时候(前提是frame的值前后发生了变化)
3 添加子视图的时候
4 滚动一个scrollview的时候
5 旋转屏幕
6 init初始化不会触发layoutSubviews
7 直接调用setLayoutSubviews

layoutSubviews 的实用(UIButton的复用,改变文字和图片的位置)

UILabel 初始化过程, 完成之后_UILabelContentLayer 覆盖了上面所有的子视图层,view管理着CALayer层
UI稳定的点

IB_DESIGNABLE
IBInspectable

drawRect
CALayer

无Xib情况

  • -(id)init
  • -(id)initWithFrame:(CGRect)frame
  • -(void)layoutSubviews

view在initWithFrame中的frame值是不准确的,我们不能在这里面设置它的frame值,只能做一些添加子控件和一些初始化的操作等。只有当view走完initWithFrame方法,才有一个完整的结构。

在label初始化的方法initWithFrame中,添加一个子view

- (id)initWithFrame:(CGRect)frame{
    
    NSLog(@"%s", __func__);
    self = [super initWithFrame:frame];
    if (self) {
        
        UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
        view.backgroundColor = [UIColor blueColor];
        [self addSubview:view];
    }
    return self;
}

而在初始化label的时候,将背景色设置为其他颜色

    eocLabel = [[EOCLabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    eocLabel.backgroundColor = [UIColor yellowColor];
    [self.view addSubview:eocLabel];

就会发现先添加的blue view被yellow覆盖


这就说明在initWithFrame走完过后,label的layer层才渲染完成,也即是说当走完initWithFrame方法后,view才会有一个完整的结构。但是试了UIButton一些空间,blue并没有被yellow覆盖,这应该是控件的底层实现有些不同。

有Xib情况

  • -(id)initWithCoder:(NSCoder *)aDecoder
  • -(void)awakeFromNib
  • -(void)layoutSubviews
  • -(void)drawRect:(CGRect)rect

initWithCoder:相当于我们用代码创建时的initWithFrame方法,只要对象是从文件解析来的, 就会调用这个方法。

awakeFromNib:从xib或者storyboard加载完毕就会调用,但是我们一般不在这里面做操作,当使用一个controller控制多个nib文件时,awakeFromNib方法会被多次调用。因此,当不使用awakeFromNib方法来完成nib对象的初始化时,需要注意此方法的多次调用对其他nib文件造成的影响。

layoutSubviews:不管是用代码创建的View还是用Xib创建的View,我们一般在这个方法里面做很多操作,比如设置准确的frame值等,但是这个方法只要当子控件frame发生变化,或者添加子控件,滚动tableView等,都会调用,所以不应做太复杂和占内存的操作。

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。
    以上1,2推荐;而3,4不提倡

方法使用注意点:

  1. 若使用UIView绘图,只能在drawRect:方法中获取相应的contextRef并绘图。如果在其他方法中获取将获取到一个invalidate 的ref并且不能用于画图。drawRect:方法不能手动显示调用,必须通过调用setNeedsDisplay 或 者 setNeedsDisplayInRect,让系统自动调该方法。
  2. 若使用calayer绘图,只能在drawInContext: 中(类似鱼drawRect)绘制,或者在delegate中的相应方法绘制。同样也是调用setNeedDisplay等间接调用以上方法
  3. 若要实时画图,不能使用gestureRecognizer,只能使用touchbegan等方法来掉用setNeedsDisplay实时刷新屏幕

Xib可视化

在代码中用到IB_DESIGNABLE和IBInspectable两个关键字,即可使Xib的控件属性,实时的显示在Xib上,而不必每次运行看效果。但是此属性改变最好写在drawRect方法中,应该改变的其实是layer层。

#import <UIKit/UIKit.h>

IB_DESIGNABLE

@interface EOCView : UIView{
    
}

@property (nonatomic, strong)IBInspectable UIColor *eocColor;
@property (nonatomic, assign)IBInspectable float widthStroke;

@end

.m文件中

- (void)drawRect:(CGRect)rect{
    
    NSLog(@"%s", __func__);
  
    CGFloat centerX = (self.bounds.size.width - self.bounds.origin.x) / 2;
    CGFloat centerY = (self.bounds.size.height - self.bounds.origin.y) / 2;

    UIBezierPath *path = [[UIBezierPath alloc] init];
    // 添加一个圆形
    [path addArcWithCenter:CGPointMake(centerX, centerY) radius:50 startAngle:0 endAngle:360 clockwise:YES];
    
    // 设置线条宽度
    path.lineWidth = _widthStroke;
    // 设置线条颜色
    [_eocColor setStroke];
    // 绘制线条
    [path stroke];
    
}

这里即便多了两个属性值,我们可以改变这里实时看到效果图

参考文章:
UIView的layoutSubviews和drawRect方法何时调用
awakeFromNib 整理摘录

相关文章

网友评论

      本文标题:控制器加载过程及UI初始化

      本文链接:https://www.haomeiwen.com/subject/eifynttx.html