你好,歡迎來到IOS教程網

 Ios教程網 >> IOS訊息 >> 關於IOS >> 使用xib開發界面

使用xib開發界面

編輯:關於IOS
純代碼寫界面有時候會降低開發效率,對於一些通用簡單的界面,例如程序設置界面,可以使用xib進行開發。
一、關於xib

1. xib和nib

xib文件可以被Xcode編譯成nib文件,xib文件本質上是一個xml文件,而nib文件就是編譯後的二進制文件,該文件將視圖等控件對象封裝了起來,而在程序運行起來後,這些對象會被激活。

xib文件本質上是一個xml文件,可以用vim或cat命令查看,例如:

$ cat ~/Desktop/JLN-1_xib/JLN-1_xib/GrayViewController.xib
<!--?xml version="1.0" encoding="UTF-8" standalone="no"?-->
<document type="com.apple.interfacebuilder3.cocoatouch.xib" version="3.0" toolsversion="6254" systemversion="14b25" targetruntime="ios.cocoatouch" propertyaccesscontrol="none" useautolayout="yes" usetraitcollections="yes">
    <dependencies>
        <plugin identifier="com.apple.interfacebuilder.ibcocoatouchplugin" version="6247">
    </plugin identifier="com.apple.interfacebuilder.ibcocoatouchplugin" version="6247"></dependencies>
    <objects>
        <placeholder placeholderidentifier="ibfilesowner" id="-1" userlabel="file's owner" customclass="grayviewcontroller">
            <connections>
                <outlet property="actionbutton" destination="edu-ds-gip" id="qav-o1-ta6">
                <outlet property="titlelabel" destination="ycj-fh-rdg" id="xj4-yo-zzp">
                <outlet property="view" destination="i5m-pr-fkt" id="sfx-zr-jgt">
            </outlet property="view" destination="i5m-pr-fkt" id="sfx-zr-jgt"></outlet property="titlelabel" destination="ycj-fh-rdg" id="xj4-yo-zzp"></outlet property="actionbutton" destination="edu-ds-gip" id="qav-o1-ta6"></connections>
         
        <placeholder placeholderidentifier="ibfirstresponder" id="-2" customclass="uiresponder">
        <view clearscontextbeforedrawing="no" contentmode="scaletofill" id="i5m-pr-fkt">
            <rect key="frame" x="0.0" y="0.0" width="300" height="44">
            <autoresizingmask key="autoresizingmask" widthsizable="yes" heightsizable="yes">
            <subviews>
                <button opaque="no" contentmode="scaletofill" fixedframe="yes" contenthorizontalalignment="center" contentverticalalignment="center" buttontype="roundedrect" linebreakmode="middletruncation" translatesautoresizingmaskintoconstraints="no" id="edu-ds-gip">
                    <rect key="frame" x="246" y="7" width="46" height="30">
                    <state key="normal" title="button">
                        <color key="titleshadowcolor" white="0.5" alpha="1" colorspace="calibratedwhite">
                     
                    <connections>
                        <action selector="action:" destination="-1" eventtype="touchupinside" id="svp-jp-gk9">
                    </action selector="action:" destination="-1" eventtype="touchupinside" id="svp-jp-gk9"></connections>
                 
                <label opaque="no" userinteractionenabled="no" contentmode="left" horizontalhuggingpriority="251" verticalhuggingpriority="251" fixedframe="yes" text="label" linebreakmode="tailtruncation" baselineadjustment="alignbaselines" adjustsfontsizetofit="no" translatesautoresizingmaskintoconstraints="no" id="ycj-fh-rdg">
                    <rect key="frame" x="129" y="11" width="42" height="21">
                    <fontdescription key="fontdescription" type="system" pointsize="17">
                    <color key="textcolor" cocoatouchsystemcolor="darktextcolor">
                    <nil key="highlightedcolor">
                 
            </nil key="highlightedcolor"></color key="textcolor" cocoatouchsystemcolor="darktextcolor"></fontdescription key="fontdescription" type="system" pointsize="17"></rect key="frame" x="129" y="11" width="42" height="21"></label opaque="no" userinteractionenabled="no" contentmode="left" horizontalhuggingpriority="251" verticalhuggingpriority="251" fixedframe="yes" text="label" linebreakmode="tailtruncation" baselineadjustment="alignbaselines" adjustsfontsizetofit="no" translatesautoresizingmaskintoconstraints="no" id="ycj-fh-rdg"></color key="titleshadowcolor" white="0.5" alpha="1" colorspace="calibratedwhite"></state key="normal" title="button"></rect key="frame" x="246" y="7" width="46" height="30"></button opaque="no" contentmode="scaletofill" fixedframe="yes" contenthorizontalalignment="center" contentverticalalignment="center" buttontype="roundedrect" linebreakmode="middletruncation" translatesautoresizingmaskintoconstraints="no" id="edu-ds-gip"></subviews>
            <color key="backgroundcolor" white="1" alpha="1" colorspace="custom" customcolorspace="calibratedwhite">
            <nil key="simulatedstatusbarmetrics">
            <nil key="simulatedtopbarmetrics">
            <nil key="simulatedbottombarmetrics">
            <freeformsimulatedsizemetrics key="simulateddestinationmetrics">
            <point key="canvaslocation" x="382" y="285">
         
    </point key="canvaslocation" x="382" y="285"></freeformsimulatedsizemetrics key="simulateddestinationmetrics"></nil key="simulatedbottombarmetrics"></nil key="simulatedtopbarmetrics"></nil key="simulatedstatusbarmetrics"></color key="backgroundcolor" white="1" alpha="1" colorspace="custom" customcolorspace="calibratedwhite"></autoresizingmask key="autoresizingmask" widthsizable="yes" heightsizable="yes"></rect key="frame" x="0.0" y="0.0" width="300" height="44"></view clearscontextbeforedrawing="no" contentmode="scaletofill" id="i5m-pr-fkt"></placeholder placeholderidentifier="ibfirstresponder" id="-2" customclass="uiresponder"></placeholder placeholderidentifier="ibfilesowner" id="-1" userlabel="file's owner" customclass="grayviewcontroller"></objects>
</document type="com.apple.interfacebuilder3.cocoatouch.xib" version="3.0" toolsversion="6254" systemversion="14b25" targetruntime="ios.cocoatouch" propertyaccesscontrol="none" useautolayout="yes" usetraitcollections="yes">
nib文件可以在程序的Build目錄下找到。

2. xib文件的若干屬性

xib文件有以下幾個重要的屬性:

xib文件名
File’s Owner
xib文件中的視圖的Class
xib文件中的視圖的Outlet指向
從哪裡加載xib,加載xib中的什麼視圖,都可以根據這幾個屬性得出。

二、Demo實踐

Demo傳送門

1. 加載xib中File’s Owner為nil的視圖

BlueView.xib

blueview_xib.png

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];
    self.blueView = views[0];
     
    // 從xib加載進來的View大小是確定的,但是該視圖在父視圖中的位置是不確定的
    // 此外,視圖中的子視圖也是原封不動地Load進來的
    CGRect rect = _blueView.frame;
    rect.origin.x += 37.5f;
    rect.origin.y += 80.0f;
    _blueView.frame = rect;
    [self.view addSubview:_blueView];
}
運行結果:

302.jpg

結論:

File’s Owner為nil的xib文件中的視圖屬於通用視圖,在工程中可以復用
從xib加載進來的View大小是確定的,但是該視圖在父視圖中的位置是不確定的,因此需要開發者自行指定
視圖中的所有子視圖會被原封不動地Load進來
2. 加載xib中File’s Owner為self的視圖

greenview_xib.png

MainViewController.m

...
@property (weak, 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主動調用Load XIB的方法,self持有的IBOutlet指向的視圖就會被初始化
    // 這裡不需要通過views[0]的方式存取視圖
    CGRect rect = _greenView.frame;
    rect.origin.x = _blueView.frame.origin.x;
    rect.origin.y = _blueView.frame.origin.y + 80.0f;
    _greenView.frame = rect;
    [self.view addSubview:_greenView];
}
運行結果:

greenview.png

結論:

File’s Owner不為nil的xib文件中的視圖屬於專用視圖,在工程中不應該被復用
只要self主動調用loadNibNamed:owner:options:方法,self持有的IBOutlet指向的視圖就會被初始化
存取xib中的視圖不用views[0]的方式,而是通過IBOutlet類型的property進行存取
3. 加載xib中File’s Owner為特定類的視圖

RedView.xib

redview_xib.png

RedViewOwner.h
@interface RedViewOwner : NSObject
@property (strong, nonatomic) IBOutlet UIView *redView;
@end
MainViewController.m
...
@property (strong, nonatomic) RedViewOwner *redViewOwner;
...
- (void)loadRedViewFromXIB {
    // RedView.xib的File's Owner是RedViewOwner類的實例,並建立了一個從該xib的View到RedViewOwner實例的IBOutlet
    // 只要通過_redViewOwner主動調用Load XIB的方法,該IBOutlet指向的視圖就會被初始化
    self.redViewOwner = [RedViewOwner new];
    [[NSBundle mainBundle] loadNibNamed:@"RedView" owner:_redViewOwner options:nil];
     
    UIView *redView = _redViewOwner.redView;
    CGRect rect = redView.frame;
    rect.origin.x = _greenView.frame.origin.x;
    rect.origin.y = _greenView.frame.origin.y + 80.0f;
    redView.frame = rect;
    [self.view addSubview:redView];
}
運行結果:

45.jpg

結論:

File’s Owner類可以封裝視圖中的各種邏輯,而不僅僅是提供視圖內容
只要通過File’s Owner類主動調用loadNibNamed:owner:options:方法,該IBOutlet指向的視圖就會被初始化
4. 加載xib中文件名和視圖類名一致的視圖(File’s Owner為nil)

YellowView.xib

yellowview_xib.png

YellowView.h/m
@interface YellowView : UIView
+ (instancetype)viewFromNIB;
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@end
@implementation YellowView
// Convenience Method
+ (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.backgroundColor = [UIColor yellowColor];
    self.titleLabel.textColor = [UIColor whiteColor];
}
@end
MainViewController.m
...
@property (strong, nonatomic) YellowView *yellowView;
...
- (void)loadYellowViewFromXIB {
    // 說明見YellowView.m的viewFromNIB方法
    self.yellowView = [YellowView viewFromNIB];
     
    CGRect rect = _yellowView.frame;
    UIView *redView = _redViewOwner.redView;
    rect.origin.x = redView.frame.origin.x;
    rect.origin.y = redView.frame.origin.y + 80.0f;
    _yellowView.frame = rect;
    [self.view addSubview:_yellowView];
}
運行結果:

46.jpg

結論:

這裡的viewFromNib方法只是對loadNibNamed:owner:options:方法的一個簡單封裝,要求的條件包括: - xib文件名和本類類名必須一致 - 這個xib文件的File’s Owner必須為空 - 這個xib文件必須只擁有一個視圖,並且該視圖的class為本類

5. 通過UIViewController的initWithNibName:bundle:方法加載xib文件中的視圖

BlackView.xib

blackview_xib_custom.png

如果BlackViewController類希望self.view就是xib文件中的View,可以在Connections頁中建立view -> File’s Owner的Outlet,如下:

blackview_xib_connections.png

BlackViewController.h/m

@interface BlackViewController : UIViewController
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
// Convenience Method
+ (instancetype)viewControllerFromNIB;
@end
@implementation BlackViewController
- (void)viewDidLoad {
    [super viewDidLoad];
     
    self.view.backgroundColor = [UIColor blackColor];
    self.titleLabel.textColor = [UIColor whiteColor];
}
+ (instancetype)viewControllerFromNIB {
    return [[BlackViewController alloc] initWithNibName:NSStringFromClass([self class]) bundle:[NSBundle mainBundle]];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end
MainViewController.m

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

blackview.png

結論:

將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設為本類。
6. 通過UIViewController+NIB加載xib文件中的View Controller類和其視圖

GrayView.xib

QQ截圖20150202102345.jpg

UIViewController+NIB.h/m

@interface UIViewController (NIB)
// 要求xib文件名和View Controller類名一致
+ (instancetype)loadFromNib;
@end
@implementation UIViewController (NIB)
+ (instancetype)loadFromNib {
    // [self class]會由調用的類決定
    Class controllerClass = [self class];
    NSLog(@"class = %@", controllerClass);
    return [[controllerClass alloc] initWithNibName:NSStringFromClass(controllerClass) bundle:[NSBundle mainBundle]];
}
@end
GrayViewController.h/m

@interface GrayViewController : UIViewController
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UIButton *actionButton;
@end
@implementation GrayViewController
- (void)viewDidLoad {
    [super viewDidLoad];
     
    self.view.backgroundColor = [UIColor grayColor];
     
    self.titleLabel.text = @"Gray View";
    self.titleLabel.textColor = [UIColor whiteColor];
    self.titleLabel.textAlignment = NSTextAlignmentCenter;
    self.titleLabel.font = [UIFont systemFontOfSize:8.5f];
     
    [self.actionButton setTitle:@"action" forState:UIControlStateNormal];
    [self.actionButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
// 推薦從XIB文件中加載View Controller的方法,這種方法可以將XIB文件中的視圖和其按鈕響應事件全部封裝在GrayViewController
// 如果GrayViewController的按鈕響應事件由MainViewController作出響應,那麼二者的耦合度就過高
// 建議:
// 單純的通用View展示,使用從xib文件加載視圖的方法,File's Owner設為nil
// 特定擁有者的View展示,從xib文件加載視圖時,File's Owner設為擁有者
// 如果視圖中有按鈕響應事件,或其它可以和用戶交互的事件,建議采用從XIB文件中加載View Controller的方法,這樣可以封裝UI展示和交互事件
- (IBAction)action:(id)sender {
    NSLog(@"action");
}
@end
MainViewController.m

...
@property (strong, nonatomic) GrayViewController *grayViewController;
...
- (void)loadGrayViewFromXIB {
    self.grayViewController = [GrayViewController loadFromNib];
     
    UIView *grayView = _grayViewController.view;
    UIView *blackView = _blackViewController.view;
    CGRect rect = grayView.frame;
    rect.origin.x = blackView.frame.origin.x;
    rect.origin.y = blackView.frame.origin.y + 80.0f;
    grayView.frame = rect;
    [self.view addSubview:grayView];
}
運行結果:

grayview.png

結論:

這裡我專門寫了一個UIViewController+NIB的category,只需要調用loadFromNib類方法就可以加載xib中的視圖。要求: - xib文件的File’s Owner必須設置為對應的View Controller類。

三、總結

在寫界面時同時混用xib和代碼可以提高效率,而對xib的使用主要體現在其專用性和通用性上。

對於一些專門的界面,例如App中的設置界面,純代碼寫難免會浪費時間,此時可以通過xib文件的拖控件方法來定制。這個xib是專用於某一個界面的,目的是提高效率。
對於一些通用的控件甚至界面,例如一個很漂亮但實現起來非常復雜的按鈕,此時可以通過load xib文件中的視圖來快速添加。這個xib對於所有視圖是共用的,目的是提高可復用性。
對於通用的xib:

如果xib只是單純的界面展示,那麼File’s Owner可以隨意。
如果xib中包含了按鈕、手勢等用戶輸入事件,那麼File’s Owner最好設置為UIViewController類的子類。
四、自問自答

以前使用xib時一直都有點疑問,xib中可以有多個視圖控件,但是從xib中load出來的是一個數組,那麼怎麼確定哪個對象對應的是哪個控件呢?

可以實踐一下:

PurpleView.xib

purpleview_xib.png

隨便在xib文件中加了幾個視圖。

接下來將其load出來看看:

MainViewController.m

- (void)logViewsFromXIB {
    NSLog(@"%s begin", __func__);
    NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"PurpleView" owner:nil options:nil];
    for (int i = 0; i < views.count; i++) {
        id obj = views[i];
        NSLog(@"%d : %@", i, [obj class]);
    }
    NSLog(@"%s end", __func__);
}
控制台輸出如下:

2015-01-09 15:03:06.629 JLN-1_xib[3139:121677] -[MainViewController logViewsFromXIB] begin
2015-01-09 15:03:06.635 JLN-1_xib[3139:121677] 0 : UIView
2015-01-09 15:03:06.635 JLN-1_xib[3139:121677] 1 : UIButton
2015-01-09 15:03:06.636 JLN-1_xib[3139:121677] 2 : UITableView
2015-01-09 15:03:06.636 JLN-1_xib[3139:121677] 3 : UILabel
2015-01-09 15:03:06.636 JLN-1_xib[3139:121677] -[MainViewController logViewsFromXIB] end
結論:

從xib中load出來的views數組中視圖對象的排列順序和xib scene中的對象排列順序一致(其實就是xml文件中元素的排序而已)。如下:

purpleview_xib_scene.png

可以將其打亂並重新運行程序查看結果。

參考資料:http://www.ifun.cc/blog/2014/02/22/ioskai-fa-zhi-xibji-qiao-jie-shao/
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved