After the past few years I found that the only manageable way for creating/maintaining view (or any UI element in more general) is to instantiate the UIView from Xib file. Creating/designing is far more intuitive in Interface Builder than write layout code, defining layout constants (dimensions, colors), or even worse introduce magic numbers to nudge the elements all around.

I’m planning to introduce 5 methods below, I’ve been used each of them in different circumstances over the times.

1. The plain way (the poor man’s method to load UIView from XIB)

This approach works only in really the case when you don’t need anything else just a view assembled in Interface Builder without any communication bindings. It has actually no any particular advantages unless it’s relatively easily understanding in the beginning of the Cocoa learning process.

It uses [NSBundle loadNibNamed:owner:options] method with no respect only to the first parameter. Just include the lines below anywhere in you controller’s implementation.

1
2
3
4
5
6
7
8
9
10
11
12
// Instantiate the nib content without any reference to it.
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:@"EPPZPlainView" owner:nil options:nil];
 
// Find the view among nib contents (not too hard assuming there is only one view in it).
UIView *plainView = [nibContents lastObject];
 
// Some hardcoded layout.
CGSize padding = (CGSize){ 22.0, 22.0 };
plainView.frame = (CGRect){padding.width, padding.height, plainView.frame.size};
 
// Add to the view hierarchy (thus retain).
[self.view addSubview:plainView];

In interface builder you don’t have to setup anything special other than a single (!) customized view that you want to instantiate in you controller. No bindings, even there is no need to specify File’s owner class. You have to write you own hardcoded layout code in return (as you may noticed above).

In interface builder you don’t have to setup anything other than a single (!) view with your static customized content.

the plain way, 第二种方法:

You need to load it using the -loadNibNamed method. -initWithNibName is only for UIViewControllers.

Add the following code to your MyCustomView init method:

NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"MyCustomView" owner:self options:nil];
UIView *mainView = [subviewArray objectAtIndex:0];
[self addSubview:mainView];

Remember, if you are initializing an object from a nib, it calls - (id)initWithCoder:(NSCoder *)aDecoder to initialize, so you'll have to override that if you are creating the MyCustomView object within the nib. If you're just doing it with initWithFrame:, then just override that and add the code above. Also, in your nib, make sure you have one top-level UIView, and place all other elements within that (that makes sure that your subviewArray only has one entry).

This will load the views from the nib and add them to the object, and should do the trick.

2. The referenced way (a bit more explicit)

This method is a next step compared to the plain way, since it defines an explicit reference to the view we need. A bit cumbersome that you have to define an outlet property in your controller class to hook up the view with. This point makes this method too specific, or can say unportable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@interface EPPZViewController ()
 
// Define an outlet for the custom view.
@property (nonatomic, weak) IBOutlet UIView *referencedView;
// An action that triggers showing the view.
-(IBAction)showReferencedView;
 
@end
 
@implementation EPPZViewController
 
-(IBAction)showReferencedView
{
    // Instantiate a referenced view (assuming outlet has hooked up in XIB).
    [[NSBundle mainBundle] loadNibNamed:@"EPPZReferencedView" owner:self options:nil];
 
    // Controller's outlet has been bound during nib loading, so we can access view trough the outlet.
    [self.view addSubview:self.referencedView];
}
 
@end

The shiny part here is that you can define a context view (actually a wrapper) in Interface Builder. That can be really useful to define the contextual layout information for the view in the XIB file (much more convenient than coded layout). But in the same time you have to be aware of the Interface Builder setup. File’s Owner must be set to the instantiating controller’s class and the referencedView outlet must be bound to the actual view you need.

You can see that File’s Owner class is set to the instantiating controller’s class (EPPZViewController) and the referencedView outlet is to be bound to the actual view you want to have a reference to.

Be warned not to hook up the controller’s view outlet to the wrapper view (nor if it feels instinctually right), since that would reassign the controller’s view at instantiation with this empty one.

This approach is also known as a UITableViewCell instantiating method (without the wrapper view), by adding a UITableViewCell to the XIB file, though, it is not the scope of the present article.

3. Connected actions (some addition for the above actually)

Having a setup like above, you can easily hook up actions to the client controller sent by objects in the custom view. This could be useful, although, it still forces the view to cooperate with a given type of controller. So just define an IBAction in the main controller like below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@interface EPPZViewController ()
 
@property (nonatomic, weak) IBOutlet UIView *referencedView;
-(IBAction)showConnectedActionsView;
-(IBAction)connectedActionsViewTouchedUp:(UIButton*) button;
 
@end
 
@implementation EPPZViewController
 
-(IBAction)showConnectedActionsView
{
    // Instantiate a referenced view (assuming outlet has hooked up in XIB).
    [[NSBundle mainBundle] loadNibNamed:@"EPPZConnectedActionsView" owner:self options:nil];
 
    // Controller's outlet has been bound during nib loading, so we can access view trough the outlet.
    [self.view addSubview:self.referencedView];
}
 
-(IBAction)connectedActionsViewTouchedUp:(UIButton*) button
{
    // Any interaction (I simply remove the custom view here).
    [button.superview removeFromSuperview];
}
 
@end

Then simply hook up a button event to the action you’ve just defined before.

4. Encapsulated instantiation (a step toward enlighting controller code)

Controller codes tend to be complicated. Period.

As you incorporate new features, your controller code immediately starts to grow, which you obviously strive to avoid. A step toward keep client code clean is to create a subclass for the custom view, and start to factor the instantiating features down.

The first trick here is to remove that File’s Owner dependency, introducing a tiny little class EPPZSubclassedViewOwner with the sole purpose of referencing the right view among XIB content. It nor even need a separate file as it is specific for this type of custom view. It lifts up the owning role from the controller’s shoulder.

1
2
3
4
5
6
7
8
9
@class EPPZSubclassedView;
@interface EPPZSubclassedViewOwner : NSObject
@property (nonatomic, weak) IBOutlet EPPZSubclassedView *subclassedView;
@end
 
@interface EPPZSubclassedView : UIView
+(void)presentInViewController:(UIViewController*) viewController;
-(IBAction)dismiss;
@end

So as a benefit, we can introduce a class method that instantiates the given view presentInViewController:, then adds it to the view hierarchy. If you need different XIBs, like separate interfaces for iPhone and iPad, you can include it here as well, instead of littering the controller’s code around.

In addition, the dismissal for the view dismiss can also moved down here, as it has nothing to do with the controller itself. In the implementation we can tackle the whole instantiating in place, you can see the owner object in action below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@implementation EPPZSubclassedViewOwner
@end
 
@implementation EPPZSubclassedView
 
+(void)presentInViewController:(UIViewController*) viewController
{
    // Instantiating encapsulated here.
    EPPZSubclassedViewOwner *owner = [EPPZSubclassedViewOwner new];
    [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:owner options:nil];
 
    // Add to the view hierarchy (thus retain).
    [viewController.view addSubview:owner.subclassedView];
}
 
-(IBAction)dismiss
{ [self removeFromSuperview]; }
 
@end

In the XIB file you’ll need to assign the appropriate classes, like mark File’s Owner as an EPPZSubclassedViewOwner instance, and EPPZSubclassedView for the view.

Connect the view to it’s reference.

As well as the button event to the action. Since the IBAction is defined in our custom view, you should hook up the action there.

As a result, you can watch the client code cleaning. Much better, with ain’t no custom view related properties in the controller.

1
2
3
4
5
6
7
8
9
10
11
12
13
@interface EPPZViewController
-(IBAction)showSubclassedView;
@end
 
@implementation EPPZViewController
 
-(IBAction)showSubclassedView
{
    // A tiny one-liner that has anything to do with the custom view.
    [EPPZSubclassedView presentInViewController:self];
}
 
@end

It started to look like a reusable code, but we might still need some communication from the view towards the controller.

5. Encapsulate everything (a really flexible, reusable way to load your custom UIView from XIB)

As we successfully separated the view from the controller above, we follow this approach regarding actions as well. To achieve this we introduce a thin protocol declaration <EPPZDecoupledViewDelegate> that introduces the features to the controller, and assure the view that controller will respond to it’s messages, just as every protocol does. It contains two calls decoupledViewTouchedUp: and decoupledViewDidDismiss: at this particular case.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@class EPPZDecoupledView;
@interface EPPZDecoupledViewOwner : NSObject
@property (nonatomic, weak) IBOutlet EPPZDecoupledView *decoupledView;
@end
 
@protocol EPPZDecoupledViewDelegate
-(void)decoupledViewTouchedUp:(EPPZDecoupledView*) decoupledView;
-(void)decoupledViewDidDismiss:(EPPZDecoupledView*) decoupledView;
@end
 
@interface EPPZDecoupledView : UIView
// Indicate that this view should be presented only controllers those implements the delegate methods.
+(void)presentInViewController:(UIViewController<EPPZDecoupledViewDelegate>*) viewController;
-(IBAction)viewTouchedUp;
-(IBAction)dismiss;
@end

The impelmentation now should keep a reference delegateViewController for the controller, so it can forward the actions. You need to indicate that the controller has to implement delegate methods, so you’ll declare the type as UIViewController <EPPZDecoupledViewDelegate>. The rest is the same as before.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@implementation EPPZDecoupledViewOwner
@end
 
@interface EPPZDecoupledView ()
@property (nonatomic, weak) UIViewController <EPPZDecoupledViewDelegate> *delegateViewController;
@end
 
@implementation EPPZDecoupledView
 
+(void)presentInViewController:(UIViewController<EPPZDecoupledViewDelegate>*) viewController
{
    // Instantiating encapsulated here.
    EPPZDecoupledViewOwner *owner = [EPPZDecoupledViewOwner new];
    [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:owner options:nil];
 
    // Pass in a reference of the viewController.
    owner.decoupledView.delegateViewController = viewController;
 
    // Add (thus retain).
    [viewController.view addSubview:owner.decoupledView];
}
 
-(IBAction)viewTouchedUp
{
    // Forward to delegate.
    [self.delegateViewController decoupledViewTouchedUp:self];
}
 
-(IBAction)dismiss
{
    [self removeFromSuperview];
 
    // Forward to delegate.
    [self.delegateViewController decoupledViewDidDismiss:self];
}
 
@end

So having this, you can setup a completely independent XIB file that knows nothing (!) about it’s context. It instantiates itself, hooks up their actions on it’s own. It is reusable, can be instantiated from any kind of UIViewController that implements its protocol, which is stated clearly in the header.

The actions themselves doing not too much here, other that they invoke the implemented delegate methods in the controller, so it can customize its own features within, a pretty straight / strict / formal delegate pattern.

To make it more readable and explicit, we can move some declarations down to the .m file, so the header for our shiny custom view is just embodies only the client needs to know about it.

1
2
3
4
5
6
7
8
9
@class EPPZDecoupledView;
@protocol EPPZDecoupledViewDelegate
-(void)decoupledViewTouchedUp:(EPPZDecoupledView*) decoupledView;
-(void)decoupledViewDidDismiss:(EPPZDecoupledView*) decoupledView;
@end
 
@interface EPPZDecoupledView : UIView
+(void)presentInViewController:(UIViewController<EPPZDecoupledViewDelegate>*) viewController;
@end

So usage in the client controller just reflects these pretty neat declarations. You have to indicate that the controller gonna implement the view’s delegate features, so you include <EPPZDecoupledViewDelegate> to the interface.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@interface EPPZViewController () <EPPZDecoupledViewDelegate>
-(IBAction)showDecoupledView;
@end
 
@implementation EPPZViewController
 
-(IBAction)showDecoupledView
{ [EPPZDecoupledView presentInViewController:self]; }
 
-(void)decoupledViewTouchedUp:(EPPZDecoupledView*) decoupledView
{ /* Whatever feature. */ }
 
-(void)decoupledViewDidDismiss:(EPPZDecoupledView*) decoupledView
{ /* Acknowledge sadly. */ }
 
@end

Tada! A beautiful UI module for your project that you can customize independently leaving the controller’s code alone. Having this clients of this class never have to know anything about the XIB content, nor hook up anything within.

Interface Builder setup is nearly the same as you may figured out, but as an overview, you can find the whole project with all the five methods at GitHub.

Inspect blog.UIView_from_XIB at

As it is common with flexible code, it needs a bit more code under the hood, but anyway sooner or later these code lines will land in a collection of reusable classes of one’s own everyday framework.

This is a rough skeleton for the method, in production these classes are likely to implement presenting/dismissal animation, a model object to configure with, sometimes some UI logic shaping a more sophisticated experience, or weak delegate messaging without explicit protocol, but this is yet another story.

5 approach to load UIView from Xib的更多相关文章

  1. Xcode5 创建模板和UIView 关联XIB

    转自:http://www.cnblogs.com/china-ldw/p/3533896.html 在做ios应用开发的过程,难免遇到要创建 子view 和 自定义view的时候,归根到底,我们需要 ...

  2. iOS 给UIView添加xib

    2017-08-25编辑:这文章有点过时了 推荐新的文章:http://www.cnblogs.com/hero11223/p/6881848.html 一段时间没敲代码,以前一些简单的都不会做了,翻 ...

  3. uiview关联xib

    1,在需要实例的地方 //加载一个uiview的作法 [LotteryInvestigationView *lotteryInvestigationView=[[[NSBundle mainBundl ...

  4. UIView创建xib

    这里有两种类都可以实现,但是推荐用Empty类来创建 (Empty): 参考链接:https://blog.csdn.net/wtdask/article/details/76439295 https ...

  5. 从Xib文件加载UIView的5种方式

    在不同的Xib文件中最容易维护的是定义的视图,因此对于从Xib文件中加载UIView来说一个方便的流程是非常重要. 在过去的几年里我发现唯一易于管理创建和维护视图(或者任何界面元素,通常会更多)方式就 ...

  6. 加载 xib 文件 UIView

    记在 UIView 的 xib 文件方式有一下几种: 一 .直接加载 xib 文件, 没有.h.m 文件 1. NSBundle 方式 NSArray *objs = [[NSBundle mainB ...

  7. Xib设计UITableViewCell然后动态加载

    转自: http://www.2cto.com/kf/201202/120764.html (注:环境Mac OS X Lion 10.7.3 + Xcode 4.2.1 + iOS SDK 5.0. ...

  8. iOS开发UI篇—xib的简单使用

    iOS开发UI篇—xib的简单使用 一.简单介绍 xib和storyboard的比较,一个轻量级一个重量级. 共同点: 都用来描述软件界面 都用Interface Builder工具来编辑 不同点: ...

  9. **IOS:xib文件解析(xib和storyboard的比较,一个轻量级一个重量级)

    使用Xcode做iOS项目,经常会和Xib文件打交道,因为Xib文件直观的展现出运行时视图的外观,所以上手非常容易,使用也很方便,但对于从未用纯代码写过视图的童鞋,多数对Xib的理解有些片面. Xib ...

随机推荐

  1. 【转】Nginx 服务器安装及配置文件详解

    1. 安装nginx 1.1 选择稳定版本 我们编译安装nginx来定制自己的模块,机器CentOS 6.2 x86_64.首先安装缺少的依赖包: # yum -y install gcc gcc-c ...

  2. Linux下通过软链接转移mysql目录,解决分区空间不足(转)

    http://darwinclub.info/wp/?p=454(转) 当存放数据库分区的空间不足时,可以采取对数据库目录进行迁移的方法,具体步骤如下:1.先关闭数据库mysqladmin -p sh ...

  3. wamp介绍

    Wamp介绍      Windows下的Apache+Mysql/MariaDB+Perl/PHP/Python,一组常用来搭建动态网站或者服务器的开源软件,本身都是各自独立的程序,但是因为常被放在 ...

  4. linux命令之chown命令

    发布:JB01   来源:脚本学堂     [大 中 小] 本文介绍下,linux系统中用于文件与目录权限管理的命令 chown命令的用法,chown将指定文件的拥有者改为指定的用户或组.有需要的朋友 ...

  5. “父窗口拖动的时候Popup不随着父窗口移动”问题的解决方案

    我们用WPF用的Popup时候会发现,当 StaysOpen=True 的时候,因为Popup不会消失,在父窗口移走的时候Popup仍旧在原地...作者在国外网站上无意间发现了这个解决方案,拿出来给大 ...

  6. PHP javascript 值互相引用(不用刷新页面)

    PHP javascript 值互相引用的问题   昨天通过EMAIL给一些公司投了简历,希望他们能给我一份工作,今天其中一家公司的人给我打电话,大意是要我做一点东西(与AJAX有关) 给他们看,我听 ...

  7. memcached-win32-1.4.4-14 help doc

    memcached-win32-1.4.4-14 cmd打开命令窗口,转到解压的目录,输入 “memcached.exe -d install”. 使用telnet命令 验证缓存服务器是否可用.tel ...

  8. MySQL安装(以程序的方式启动)zip版

    电脑环境 win10 64位 企业版 1.解压zip文件到某路径下. 2.复制目录下的my-default.ini 改名字(my.ini) 3.打开my.ini [mysqld]下面加上charact ...

  9. 50个实用的jQuery代码段让你成为更好的Web前端工程师

    本文会给你们展示50个jquery代码片段,这些代码能够给你的javascript项目提供帮助.其中的一些代码段是从jQuery1.4.2才开始支持的做法,另一些则是真正有用的函数或方法,他们能够帮助 ...

  10. The Promise of Deep Learning

    The Promise of Deep Learning By Yoshua Bengio Humans have long dreamed of creating machines that thi ...