zoukankan      html  css  js  c++  java
  • UI控件初始化问题:initWithFrame和initWithCoder、aweakFromNib的执行

    在iOS学习和程序开发过程中,我们经常会遇到一些自定义UI控件或控制器在初始化时出现问题,尤其在大家刚开始接触时,几种初始化方法的作用以及调用的时机往往容易混淆,这也跟我们对iOS程序设计中,类的创建和实例化的过程了解不透彻有关系。本文用一些小例子来简单梳理一下几者的关系,后面再陆续讨论一些复杂情况的深入对比。

    问题: 一、什么时候用initWithFrame,什么时候用aweakFromNib、initWithCoder

            二、在初始化时控件自身的frame何时能获得?layoutSubViews何时调用

    首先,我们实例化一个(控件类型)对象可以有多种方式:

    (1)纯代码创建。创建自定义的UI控件类,然后实例化该类型的对象。

    (2)通过IB(Interface Builder)创建,就是俗称的“拖线”。当我们创建好xib文件的时候,就相当于创建好了控件类,但是如果不实例化,也是没有用的,所以需要加载,这里用loadNibName来加载(实例化)UI控件。

    1、搭建实验环境A,代码创建控件(TestCodeingView继承自UIView)

    -(void)loadFromCoding
    {
        TestCodeingView * viewCoding = [[TestCodeingView alloc]init]; 
        viewCoding.frame=CGRectMake(100, 100, 200, 200);                                  
        viewCoding.backgroundColor=[UIColor greenColor]; 
        [self.view addSubview:viewCoding];
    }
    
    在TestCodeingView类中对以下方法进行重写
    
    -(instancetype)init
    {
        self=[super init];
        NSLog(@" init =====> 执行了");
        NSLog(@"此时view的frame====》 %@",NSStringFromCGRect(self.frame));
        return self;
        
    }
    
    -(instancetype)initWithFrame:(CGRect)frame
    {
        self=[super initWithFrame:frame];
        NSLog(@" initWithFrame =====> 执行了");
        NSLog(@"此时view的frame====》 %@",NSStringFromCGRect(self.frame));
        return self; 
    }
    
    -(instancetype)initWithCoder:(NSCoder *)aDecoder
    {
        self=[super initWithCoder:aDecoder];  
        NSLog(@" initWithCoder =====> 执行了");
        return self;
    }
    -(void)awakeFromNib
    {
        NSLog(@" awakeFromNib =====> 执行了");
    }
    
    -(void)layoutSubviews
    {
       NSLog(@" layoutSubviews =====> 执行了");
       NSLog(@"此时view的frame====》 %@",NSStringFromCGRect(self.frame));
     
    }

    运行结果:

    然后更改部分代码:

    -(instancetype)initWithFrame:(CGRect)frame
    {
        self=[super initWithFrame:frame];
        NSLog(@" initWithFrame =====> 执行了");
        NSLog(@"此时view的frame====》 %@",NSStringFromCGRect(self.frame));
        UILabel * label = [[UILabel alloc]init];
        label.text=@"我是新建的label";
        label.backgroundColor=[UIColor orangeColor];
        self.label=label;
        [self addSubview:label];
        
        return self;   
    }
    
    -(void)layoutSubviews
    { 
        NSLog(@" layoutSubviews =====> 执行了");   
        NSLog(@"此时view的frame====》 %@",NSStringFromCGRect(self.frame));
        self.label.frame = CGRectMake((self.frame.size.width-150)/2,self.frame.size.height/2, 150, 30);
        
    }

    运行结果:

    小结一下:(1)纯代码创建的UI控件不执行aweakFromNib方法和 initWithCoder方法。 

         (2)layoutSubciews方法在控件初始化完成后(自身和子控件的实例化结束)调用,方法中能获得到当前控件的frame,以便于给子控件布局。如有子控件,调用两次。

         (3)系统在调用以上方法时,有着特定的先后顺序。

    2、搭建实验环境B,Xib创建控件

    通过xib加载自定义UI控件,如下图,TestXibView类为手动创建的UI控件类,继承自UIView

    -(void)loadFromXib
    {
        TestXibView * viewXib = [[[NSBundle mainBundle]loadNibNamed:@"testXibView" owner:nil options:nil] lastObject];  
        viewXib.center=self.view.center;
        [self.view addSubview:viewXib];
    }

    在TestCodeingView类中对以下方法进行重写

    -(instancetype)init
    {
        self=[super init];
        NSLog(@" init =====> 执行了");    
        return self;
    
    }
    
    
    -(instancetype)initWithFrame:(CGRect)frame
    {
        self=[super initWithFrame:frame];  
        NSLog(@" initWithFrame =====> 执行了");
        
        return self;
       
    }
    
    
    
    -(instancetype)initWithCoder:(NSCoder *)aDecoder
    {
        self=[super initWithCoder:aDecoder];   
        NSLog(@" initWithCoder =====> 执行了"); 
        return self;
    
    }
    
    
    -(void)awakeFromNib
    {
        NSLog(@" awakeFromNib =====> 执行了");  
    }
    
    
    -(void)layoutSubviews
    {
        NSLog(@" layoutSubviews =====> 执行了");
    }

    运行结果:

     

    更改部分代码,对Xib加载的控件使用代码进行修改 (添加了一个子控件和更改背景颜色):

    -(instancetype)initWithCoder:(NSCoder *)aDecoder
    {
        self=[super initWithCoder:aDecoder];
        
        NSLog(@" initWithCoder =====> 执行了");
        
        UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 150, 30)];
        label.text=@"我是新建的label";
        label.backgroundColor=[UIColor orangeColor];
        label.center=CGPointMake(self.center.x, self.frame.size.height-30);
        [self addSubview:label];
        
        return self;
    
    }
    
    -(void)awakeFromNib
    {
       NSLog(@" awakeFromNib =====> 执行了"); 
       self.backgroundColor=[UIColor yellowColor];
        
    }

    运行结果:

     

    小结一下:(1)通过Xib创建UI控件,不会调用init和initwith方法。

                  (2创建一个控件类,和xib关联,是可以修改Xib中的属性的。

                  (3一样会调用layoutSubViews方法

         (4因为通过拖线和配置,已经固定了控件的大小和布局,所以frame可以获得

                  (5initWithCoder和 aweakFromNib 在这里作用相同,都被系统调用

      

    总结及延伸:

    当我们弄清楚控制器加载的各种情况后,相对于代码,使用IB和xib文件来组织UI,可以省下大量代码和时间,从而得到更快的开发速度;同时,Xib最大的问题在于其设置往往并非最终设置,在代码中你将有机会覆盖你在xib文件中进行的UI设计,造成错误和混乱。

    说了好多,总结一下也无非几句话:

    1、用Xib创建控件,对于控件的后续操作都写在initWithCoder或aweakFromNib方法中;

    2、纯代码写创建的控件,对于控件的后续操作都写在initWithFrame方法中;

    3、添加子控件时,注意布局(frame的获得),合理灵活的使用xib加载控件;

    4、至于initWithCoder和aweakFromNib的区别在后面再做讨论(关于通过xib加载控制器)。

  • 相关阅读:
    LeetCode 230. 二叉搜索树中第K小的元素(Kth Smallest Element in a BST)
    LeetCode 216. 组合总和 III(Combination Sum III)
    LeetCode 179. 最大数(Largest Number)
    LeetCode 199. 二叉树的右视图(Binary Tree Right Side View)
    LeetCode 114. 二叉树展开为链表(Flatten Binary Tree to Linked List)
    LeetCode 106. 从中序与后序遍历序列构造二叉树(Construct Binary Tree from Inorder and Postorder Traversal)
    指针变量、普通变量、内存和地址的全面对比
    MiZ702学习笔记8——让MiZ702变身PC的方法
    你可能不知道的,定义,声明,初始化
    原创zynq文章整理(MiZ702教程+例程)
  • 原文地址:https://www.cnblogs.com/cleven/p/5239856.html
Copyright © 2011-2022 走看看