zoukankan      html  css  js  c++  java
  • iOS

    前言

    • xib 文件可以被 Xcode 编译成 nib 文件,xib 文件本质上是一个 xml 文件,而 nib 文件就是编译后的二进制文件,该文件将视图等控件对象封装了起来,而在程序运行起来后,这些对象会被激活。xib 可以用 vim 或 cat 命令查看。nib 文件可以在程序的 Build 目录下找到。

    • xib 文件有以下几个重要的属性,从哪里加载 xib,加载 xib 中的什么视图,都可以根据这几个属性得出。

      • xib 文件名
      • File’s Owner
      • xib 文件中的视图的 Class
      • xib 文件中的视图的 Outlet 指向
    • 在写界面时同时混用 xib 和代码可以提高效率,而对 xib 的使用主要体现在其专用性和通用性上。

    • 对于一些专门的界面,例如 App 中的设置界面,纯代码写难免会浪费时间,此时可以通过 xib 文件的拖控件方法来定制。这个 xib 是专用于某一个界面的,目的是提高效率。

    • 对于一些通用的控件甚至界面,例如一个很漂亮但实现起来非常复杂的按钮,此时可以通过 load xib 文件中的视图来快速添加。这个 xib 对于所有视图是共用的,目的是提高可复用性。

    • 对于通用的 xib:

      • 如果 xib 只是单纯的界面展示,那么 File’s Owner 可以随意。
      • 如果 xib 中包含了按钮、手势等用户输入事件,那么 File’s Owner 最好设置为 UIViewController 类的子类。
    • xib 中可以有多个视图控件,从 xib 中 load 出来的 views 数组中视图对象的排列顺序和 xib scene 中的对象排列顺序一致(其实就是 xml 文件中元素的排序而已)。

    1、xib 文件加载

    • 1)xib 文件加载方式

      • 1> 方法 1

        	// Test 为 xib 文件名
        	NSArray *objs = [[NSBundle mainBundle] loadNibNamed:@"Test" owner:nil options:nil];
        	
        	[self.view addSubview:objs[1]];
        
      • 2> 方法 2

            // 一个 UINib 对象就代表一个 xib 文件
            /*
            	一般情况下,bundle 参数传 nil,默认就是 mainBundle
            */
            UINib *nib = [UINib nibWithNibName:@"Test" bundle:[NSBundle mainBundle]];
        
            NSArray *objs = [nib instantiateWithOwner:nil options:nil];
            [self.view addSubview:objs[1]];
        
    • 2)使用 xib 自定义 view 的步骤

      • 新建自定义控件类

        Xib9

      • 新建 xib 文件(文件名建议和自定义控件类的类名一致)

        Xib10

      • 修改 xib 中控件绑定的类名

        Xib11

      • 在类扩展中增加子控件的属性,关联 xib 中的子控件。封装 xib 的加载过程,增加模型属性,在模型属性 setter 方法中设置数据到子控件上。

    1.1 加载 xib 中 File’s Owner 为 nil 的视图

    • Objective-C

      • BlueView.xib

        Xib1

        • 由于 BlueView 会放到其它 View 上作为 subview,所以这儿 size 是 Freeform, Status Bar 是 None。
      • MainViewController.m

        	@property (strong, nonatomic) UIView *blueView;
        
        	- (void)loadBlueViewFromXIB {
            
            	// BlueView.xib 的 File's Owner 为 nil
            
            	NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"BlueView" owner:nil options:nil];
            
            	_blueView = views[0];
            	[self.view addSubview:_blueView];
            
            	// 从 xib 加载进来的 View 大小是确定的,但是该视图在父视图中的位置是不确定的。此外,视图中的子视图也是原封不动地 Load 进来的
            
            	CGRect frame = _blueView.frame;
            	frame.origin.x += 50.0f;
            	frame.origin.y += 80.0f;
            	_blueView.frame = frame;
        	}
        
    • Swift

      • BlueView.xib

        同 Objective-C 设置

      • MainViewController.swift

        	var blueView:UIView!
        
        	func loadBlueViewFromXIB() {
        
            	// BlueView.xib 的 File's Owner 为 nil
            
            	let views = NSBundle.mainBundle().loadNibNamed("BlueView", owner: nil, options: nil)
            
            	blueView = views[0] as! UIView
            	self.view.addSubview(blueView)
            
            	// 从 xib 加载进来的 View 大小是确定的,但是该视图在父视图中的位置是不确定的。此外,视图中的子视图也是原封不动地 Load 进来的
            
            	var frame = blueView.frame
            	frame.origin.x += 50.0
            	frame.origin.y += 80.0
            	blueView.frame = frame
        	}
        
    • 结论:

      • File’s Owner 为 nil 的 xib 文件中的视图属于通用视图,在工程中可以复用。
      • 从 xib 加载进来的 View 大小是确定的,但是该视图在父视图中的位置是不确定的,因此需要开发者自行指定。
      • 视图中的所有子视图会被原封不动地 Load 进来。

    1.2 加载 xib 中 File’s Owner 为 self 的视图

    • Objective-C

      • GreenView.xib

        Xib2

      • MainViewController.m

        	@property (strong, nonatomic) IBOutlet UIView *greenView;
        
        	- (void)loadGreenViewFromXIB {
            
            	// GreenView.xib 的 File's Owner 设为 self,并建立了一个从该 xib 的 View 到 self 的 IBOutlet greenView
            
            	[[NSBundle mainBundle] loadNibNamed:@"GreenView" owner:self options:nil];
            
            	[self.view addSubview:_greenView];
            
            	// 只要 self 主动调用 Load XIB 的方法,self 持有的 IBOutlet 指向的视图就会被初始化。这里不需要通过 views[0] 的方式存取视图
            
            	CGRect frame = _greenView.frame;
            	frame.origin.x = _blueView.frame.origin.x;
            	frame.origin.y = _blueView.frame.origin.y + 80.0f;
            	_greenView.frame = frame;
        	}
        
    • Swift

      • GreenView.xib

        同 Objective-C 设置

      • MainViewController.swift

        	@IBOutlet var greenView: UIView!
        
        	func loadGreenViewFromXIB() {
        
            	// GreenView.xib 的 File's Owner 设为 self,并建立了一个从该 xib 的 View 到 self 的 IBOutlet greenView
            
            	NSBundle.mainBundle().loadNibNamed("GreenView", owner: self, options: nil)
            
            	self.view.addSubview(greenView)
            
            	// 只要 self 主动调用 Load XIB 的方法,self 持有的 IBOutlet 指向的视图就会被初始化。这里不需要通过 views[0] 的方式存取视图
            
            	var frame = greenView.frame
            	frame.origin.x = blueView.frame.origin.x
            	frame.origin.y = blueView.frame.origin.y + 80.0
            	greenView.frame = frame
        	}
        
    • 结论:

      • File’s Owner 不为 nil 的 xib 文件中的视图属于专用视图,在工程中不应该被复用。
      • 只要 self 主动调用 loadNibNamed:owner:options: 方法,self 持有的 IBOutlet 指向的视图就会被初始化。
      • 存取 xib 中的视图不用 views[0] 的方式,而是通过 IBOutlet 类型的 property 进行存取。

    1.3 加载 xib 中 File’s Owner 为特定类的视图

    • Objective-C

      • RedView.xib

        Xib3

      • RedViewOwner.h

        	@property (strong, nonatomic) IBOutlet UIView *redView;
        
      • MainViewController.m

        	@property (strong, nonatomic) RedViewOwner *redViewOwner;
        	@property (strong, nonatomic) UIView *redView;
        
        	- (void)loadRedViewFromXIB {
            
            	// RedView.xib 的 File's Owner 是 RedViewOwner 类的实例,并建立了一个从该 xib 的 View 到 RedViewOwner 实例的 IBOutlet。
            	// 只要通过 _redViewOwner 主动调用 Load XIB 的方法,该 IBOutlet 指向的视图就会被初始化
            
            	_redViewOwner = [RedViewOwner new];
            
            	[[NSBundle mainBundle] loadNibNamed:@"RedView" owner:_redViewOwner options:nil];
            
            	_redView = _redViewOwner.redView;
            	[self.view addSubview:_redView];
            
            	CGRect frame = _redView.frame;
            	frame.origin.x = _greenView.frame.origin.x;
            	frame.origin.y = _greenView.frame.origin.y + 80.0f;
            	_redView.frame = frame;
        	}
        
    • Swift

      • RedView.xib

        同 Objective-C 设置

      • RedViewOwner.swift

        	@IBOutlet var redView: UIView!
        
      • MainViewController.swift

        	var redViewOwner:RedViewOwner!
        	var redView:UIView!
        
        	func loadRedViewFromXIB() {
        
            	// RedView.xib 的 File's Owner 是 RedViewOwner 类的实例,并建立了一个从该 xib 的 View 到 RedViewOwner 实例的 IBOutlet。
            	// 只要通过 _redViewOwner 主动调用 Load XIB 的方法,该 IBOutlet 指向的视图就会被初始化
            
            	redViewOwner = RedViewOwner()
            
            	NSBundle.mainBundle().loadNibNamed("RedView", owner: redViewOwner, options: nil)
            
            	redView = redViewOwner.redView
            	self.view.addSubview(redView)
            
            	var frame = redView.frame
            	frame.origin.x = greenView.frame.origin.x
            	frame.origin.y = greenView.frame.origin.y + 80.0
            	redView.frame = frame
        	}
        
    • 结论:

      • File’s Owner 类可以封装视图中的各种逻辑,而不仅仅是提供视图内容。
      • 只要通过 File’s Owner 类主动调用 loadNibNamed:owner:options: 方法,该 IBOutlet 指向的视图就会被初始化。

    1.4 加载 xib 中文件名和视图类名一致的视图(File’s Owner 为 nil)

    • Objective-C

      • YellowView.xib

        Xib4

        File’s Owner 为 nil

      • YellowView.h

        	// Convenience Method
        	+ (instancetype)viewFromNIB;
        
      • YellowView.m

        	+ (instancetype)viewFromNIB {
            
            	// 加载 xib 中的视图,其中 xib 文件名和本类类名必须一致
            	// 这个 xib 文件的 File's Owner 必须为空
            	// 这个 xib 文件必须只拥有一个视图,并且该视图的 class 为本类
            
        		NSArray *views = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:nil options:nil];
            	return views[0];
        	}
        
        	- (void)awakeFromNib {
            
            	// self 就是 xib 文件中的 View
            	self.backgroundColor = [UIColor yellowColor];
        	}
        
      • MainViewController.m

        	@property (strong, nonatomic) YellowView *yellowView;
        
        	- (void)loadYellowViewFromXIB {
            
            	// 说明见 YellowView.m 的 viewFromNIB 方法
            
            	_yellowView = [YellowView viewFromNIB];
            
            	[self.view addSubview:_yellowView];
            
            	CGRect frame = _yellowView.frame;
            	frame.origin.x = _redView.frame.origin.x;
            	frame.origin.y = _redView.frame.origin.y + 80.0f;
            	_yellowView.frame = frame;
        	}
        
    • Swift

      • YellowView.xib

        同 Objective-C 设置

      • YellowView.swift

         	class func viewFromNIB() -> AnyObject {
        
            	// 加载 xib 中的视图,其中 xib 文件名和本类类名必须一致
            	// 这个 xib 文件的 File's Owner 必须为空
            	// 这个 xib 文件必须只拥有一个视图,并且该视图的 class 为本类
            
            	let views = NSBundle.mainBundle().loadNibNamed("YellowView", owner: nil, options: nil)
        
            	return views[0]
        	}
        
        	override func awakeFromNib() {
            
            	// self 就是 xib 文件中的 View
            	self.backgroundColor = UIColor.yellowColor()
        	}
        
      • MainViewController.swift

        	var yellowView:YellowView!
        
        	func loadYellowViewFromXIB() {
        
            	// 说明见 YellowView.m 的 viewFromNIB 方法
            
            	yellowView = YellowView.viewFromNIB() as! YellowView
            
            	self.view.addSubview(yellowView)
            
            	var frame = yellowView.frame
            	frame.origin.x = redView.frame.origin.x
            	frame.origin.y = redView.frame.origin.y + 80.0
            	yellowView.frame = frame
        	}
        
    • 结论:

      • 这里的 viewFromNib 方法只是对 loadNibNamed:owner:options: 方法的一个简单封装,要求的条件包括:
        • xib 文件名和本类类名必须一致。
        • 这个 xib 文件的 File’s Owner 必须为空。
        • 这个 xib 文件必须只拥有一个视图,并且该视图的 class 为本类。

    1.5 通过 UIViewController 的 initWithNibName:bundle: 方法加载 xib 文件中的视图

    • Objective-C

      • BlackViewController.xib

        Xib5

        Xib6

        • 如果 BlackViewController 类希望 self.view 就是 xib 文件中的 View,可以在 Connections 页中建立 view -> File’s Owner 的 Outlet。由 File’s Owner 右键拖动到 view 上。
      • BlackViewController.h

        	//  Conveniece Method
        	+ (instancetype)viewControllerFromNIB;
        
      • BlackViewController.m

        	+ (instancetype)viewControllerFromNIB {
            
            	return [[BlackViewController alloc] initWithNibName:NSStringFromClass([self class]) bundle:[NSBundle mainBundle]];
        	}
        
        	- (void)viewDidLoad {
            	[super viewDidLoad];
            
            	// self.view 就是 xib 文件中的 View
            	self.view.backgroundColor = [UIColor blackColor];
        	}
        
      • MainViewController.m

        	@property (strong, nonatomic) BlackViewController *blackViewController;
        	@property (strong, nonatomic) UIView *blackView;
        
        	- (void)loadBlackViewFromXIB {
            
            	_blackViewController = [[BlackViewController alloc] initWithNibName:@"BlackViewController" bundle:[NSBundle mainBundle]];
            
            	// 或使用 Conveniece Method,但要求 xib 文件名和 View Controller 类名一致
            	_blackViewController = [BlackViewController viewControllerFromNIB];
            
            	_blackView = _blackViewController.view;
            	[self.view addSubview:_blackView];
            
            	CGRect frame = _blackView.frame;
            	frame.origin.x = _yellowView.frame.origin.x;
            	frame.origin.y = _yellowView.frame.origin.y + 80.0f;
            	_blackView.frame = frame;
        	}
        
    • Swift

      • BlackViewController.xib

        同 Objective-C 设置

      • BlackViewController.swift

        	class func viewControllerFromNIB() -> AnyObject {
        
            	return BlackViewController(nibName: "BlackViewController", bundle: NSBundle.mainBundle())
        	}
        
        	override func viewDidLoad() {
            	super.viewDidLoad()
        
            	self.view.backgroundColor = UIColor.blackColor()
        	}
        
      • MainViewController.swift

        	var blackViewController:BlackViewController!
        	var blackView:UIView!
        
        	func loadBlackViewFromXIB() {
        
            	blackViewController = BlackViewController(nibName: "BlackViewController", bundle: NSBundle.mainBundle())
            
            	// 或使用 Conveniece Method,但要求 xib 文件名和 View Controller 类名一致
            	blackViewController = BlackViewController.viewControllerFromNIB() as! BlackViewController
            
            	blackView = blackViewController.view;
            	self.view.addSubview(blackView)
            
            	var frame = blackView.frame
            	frame.origin.x = yellowView.frame.origin.x
            	frame.origin.y = yellowView.frame.origin.y + 80.0
            	blackView.frame = frame
        	}
        
    • 结论:

      • 将 xib 的 File’s Owner 设成一个 UIViewController 子类,可以将这个 xib 文件的视图展示和外部响应事件(例如点击一个按钮触发的点击事件,该视图的手势事件等)全部封装在一个 View Controller 中,如果把按钮的点击事件封装在一个 UIView 类中,貌似破坏了 MVC 模式,因此最好将 xib 的 File’s Owner 设成一个 UIViewController 子类,该类可以通过 addChildViewController 方法将其添加到现有的 View Controller 上。如果只是希望加载视图,可以通过 viewcontroller.view 存取。

      • 如果希望 ViewControllerA 加载并响应 aXIBView 中的按钮点击事件,这时必须建立一个 aXIBView 到 ViewControllerA 的 IBAction,如果 ViewControllerA 需要拥有多个这样的 XIB,那么 ViewControllerA 会变得非常的庞大,此时可以通过为每一个 XIB 设置一个 ViewController,再让 ViewControllerA 加载这些 Child View Controllers,这样可以将这些事件的响应职责和视图的描绘工作分派给专门的 Child View Controller,在减小 ViewControllerA 体积的同时,也可以提高各个 xib 的可复用性。

      • 这里的 viewControllerFromNIB 方法其实就是 initWithNibName:bundle: 方法的一个简单封装,要求:xib 的 File’s Owner 设为本类。

    1.6 通过 UIViewController + NIB 加载 xib 文件中的 View Controller 类和其视图

    • Objective-C

      • GrayViewController.xib

        Xib7

        Xib8

      • UIViewController+NIB.h

        	@interface UIViewController (NIB)
        
        	// 要求 xib 文件名和 View Controller 类名一致
        	+ (instancetype)loadFromNib;
        
        	@end
        
      • UIViewController+NIB.m

        	@implementation UIViewController (NIB)
        
        	+ (instancetype)loadFromNib {
            
            	// [self class] 会由调用的类决定
        		return [[[self class] alloc] initWithNibName:NSStringFromClass([self class]) bundle:[NSBundle mainBundle]];
        	}
        
        	@end
        
      • GrayViewController.h

        	@property (weak, nonatomic) IBOutlet UIButton *actionButton;
        
      • GrayViewController.m

        	- (void)viewDidLoad {
            	[super viewDidLoad];
            
            	self.view.backgroundColor = [UIColor grayColor];
        	}
        
        	// 推荐从 xib 文件中加载 View Controller 的方法,这种方法可以将 xib 文件中的视图和其按钮响应事件全部封装在 GrayViewController
        	// 如果 GrayViewController 的按钮响应事件由 MainViewController 作出响应,那么二者的耦合度就过高
        	// 建议:
        	// 单纯的通用 View 展示,使用从 xib 文件加载视图的方法,File's Owner 设为 nil
        	// 特定拥有者的 View 展示,从 xib 文件加载视图时,File's Owner 设为拥有者
        	// 如果视图中有按钮响应事件,或其它可以和用户交互的事件,建议采用从 xib 文件中加载 View Controller 的方法,这样可以封装 UI 展示和交互事件
        
        	- (IBAction)action:(UIButton *)sender {
            
            	NSLog(@"action");
        	}
        
      • MainViewController.m

        	@property (strong, nonatomic) GrayViewController *grayViewController;
        	@property (strong, nonatomic) UIView *grayView;
        
        	- (void)loadGrayViewFromXIB {
            
            	_grayViewController = [GrayViewController loadFromNib];
            
            	_grayView = _grayViewController.view;
            	[self.view addSubview:_grayView];
            
            	CGRect frame = _grayView.frame;
            	frame.origin.x = _blackView.frame.origin.x;
            	frame.origin.y = _blackView.frame.origin.y + 80.0f;
            	_grayView.frame = frame;
        	}
        
    • 结论:

      • 这里我专门写了一个 UIViewController+NIB 的 category,只需要调用 loadFromNib 类方法就可以加载 xib 中的视图。要求:
        • xib 文件的 File’s Owner 必须设置为对应的 View Controller 类。
  • 相关阅读:
    DNS解析的并发性
    Pycharm(Jetbrains IDE)Debian buster Navigate Back/Forward (Ctrl+Alt+Left/Right)不好使的解决方法
    Linux命令行登录时的提示信息
    cmake编译Qt5
    cmake使用ccache
    bash 脚本所在文件夹
    gnome desktop
    gnome caps lock 和 num lock 键状态
    oracle 日期、月份处理
    独夜行
  • 原文地址:https://www.cnblogs.com/QianChia/p/5759401.html
Copyright © 2011-2022 走看看