我试着做一些复杂的事情,但应该是可能的。所以这里有一个对你们所有专家的挑战(这个论坛是由你们很多人组成的:))。

我正在创建一个问卷“组件”,我想加载在一个navigationcontroller(我的QuestionManagerViewController)。“组件”是一个“空的”UIViewController,它可以根据需要回答的问题加载不同的视图。

我的做法是:

Create Question1View object as a UIView subclass, defining some IBOutlets. Create (using Interface Builder) the Question1View.xib (HERE IS WHERE MY PROBLEM PROBABLY IS). I set both the UIViewController and the UIView to be of class Question1View. I link the outlets with the view's component (using IB). I override the initWithNib of my QuestionManagerViewController to look like this: - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) { // Custom initialization } return self; }

当我运行代码时,我得到这个错误:

2009-05-14 15:05:37.152 iMobiDines[17148:20b] ***由于未捕获异常'NSInternalInconsistencyException'而终止应用程序,原因:'-[UIViewController _loadViewFromNibNamed:bundle:] loaded the "Question1View" nib but the view outlet was not set.'

我确信有一种方法可以使用nib文件加载视图,而不需要创建一个viewController类。


当前回答

我做了一个我喜欢的分类:

UIView + NibInitializer.h

#import <UIKit/UIKit.h>

@interface UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil;
@end

UIView + NibInitializer.m

#import "UIView+NibInitializer.h"

@implementation UIView (NibInitializer)

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    if (!nibNameOrNil) {
        nibNameOrNil = NSStringFromClass([self class]);
    }
    NSArray *viewsInNib = [[NSBundle mainBundle] loadNibNamed:nibNameOrNil
                                                        owner:self
                                                      options:nil];
    for (id view in viewsInNib) {
        if ([view isKindOfClass:[self class]]) {
            self = view;
            break;
        }
    }
    return self;
}

@end

然后,像这样调用:

MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil];

如果你的nib不是你的类名,请使用一个nib名。

为了在你的子类中覆盖它以获得额外的行为,它可以看起来像这样:

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    self = [super initWithNibNamed:nibNameOrNil];
    if (self) {
        self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
    }
    return self;
}

其他回答

我也想做一些类似的事情,这是我发现的: (SDK 3.1.3)

我有一个视图控制器a(本身由一个导航控制器拥有),它在一个按钮按下加载VC B:

在AViewController.m

BViewController *bController = [[BViewController alloc] initWithNibName:@"Bnib" bundle:nil];
[self.navigationController pushViewController:bController animated:YES];
[bController release];

现在VC B有它的接口从Bnib,但当一个按钮被按下,我想去一个“编辑模式”有一个独立的UI从不同的nib,但我不想要一个新的VC的编辑模式,我想新的nib与我现有的B VC相关联。

在BViewController中。M(按按钮方式)

NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"EditMode" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
self.editView = theEditView;
[self.view addSubview:theEditView];

然后按下另一个按钮(退出编辑模式):

[editView removeFromSuperview];

我又变回了原来的Bnib

这很好,但请注意我的EditMode。nib中只有一个顶级obj,一个UIView obj。 不管这个nib中的文件所有者是设置为BViewController还是默认的NSObject,但要确保文件所有者中的视图出口没有设置为任何东西。 如果是,那么我得到一个exc_bad_access崩溃,xcode继续加载6677个堆栈帧 显示一个内部UIView方法被反复调用…看起来像一个无限循环。 (视图出口是设置在我原来的Bnib然而)

希望这能有所帮助。

我发现Aaron Hillegass(作者,讲师,可可忍者)的这篇博客非常有启发性。即使您不采用他修改后的方法通过指定的初始化器加载NIB文件,您也至少可以更好地理解正在进行的过程。我最近一直在用这个方法,而且非常成功!

@AVeryDev

6)将加载的视图附加到视图控制器的视图:

[self.view addSubview:myViewFromNib];

据推测,有必要将其从视图中删除以避免内存泄漏。

澄清一下:视图控制器有几个iboutlet,其中一些连接到原始nib文件中的项(像往常一样),一些连接到加载的nib中的项。两个nib都有相同的所有者类。加载的视图覆盖了原始视图。

提示:在加载的笔尖中设置主视图的不透明度为零,那么它将不会从原始笔尖中模糊项目。

我最终为这个添加了一个类别到UIView:

 #import "UIViewNibLoading.h"

 @implementation UIView (UIViewNibLoading)

 + (id) loadNibNamed:(NSString *) nibName {
    return [UIView loadNibNamed:nibName fromBundle:[NSBundle mainBundle] retainingObjectWithTag:1];
 }

 + (id) loadNibNamed:(NSString *) nibName fromBundle:(NSBundle *) bundle retainingObjectWithTag:(NSUInteger) tag {
    NSArray * nib = [bundle loadNibNamed:nibName owner:nil options:nil];
    if(!nib) return nil;
    UIView * target = nil;
    for(UIView * view in nib) {
        if(view.tag == tag) {
            target = [view retain];
            break;
        }
    }
    if(target && [target respondsToSelector:@selector(viewDidLoad)]) {
        [target performSelector:@selector(viewDidLoad)];
    }
    return [target autorelease];
 }

 @end

这里的解释:在ios和mac中viewcontroller是更少的视图加载

Swift用户可设计选项:

Create a custom UIView subclass and a xib files, that we will name after our own class name: in our case MemeView. Inside the Meme View class remember to define it as designable with the @IBDesignable attribute before the class declaration Rember to set the File’s Owner in the xib with our custom UIView subclass in Indetity Inspector panel In the xib file now we can build our interface, make constraints, create outlets, actions etc. We need to implement few methods to our custom class to open the xib once initialized class XibbedView: UIView { weak var nibView: UIView! override convenience init(frame: CGRect) { let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last! self.init(nibName: nibName) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last! let nib = loadNib(nibName) nib.frame = bounds nib.translatesAutoresizingMaskIntoConstraints = false addSubview(nib) nibView = nib setUpConstraints() } init(nibName: String) { super.init(frame: CGRectZero) let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last! let nib = loadNib(nibName) nib.frame = bounds nib.translatesAutoresizingMaskIntoConstraints = false addSubview(nib) nibView = nib setUpConstraints() } func setUpConstraints() { ["V","H"].forEach { (quote) -> () in let format = String(format:"\(quote):|[nibView]|") addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: [], metrics: nil, views: ["nibView" : nibView])) } } func loadNib(name: String) -> UIView { let bundle = NSBundle(forClass: self.dynamicType) let nib = UINib(nibName: name, bundle: bundle) let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView return view } } In our custom class we can also define some inspecatable properties to have full control over them from interface builder @IBDesignable class MemeView: XibbedView { @IBInspectable var memeImage: UIImage = UIImage() { didSet { imageView.image = memeImage } } @IBInspectable var textColor: UIColor = UIColor.whiteColor() { didSet { label.textColor = textColor } } @IBInspectable var text: String = "" { didSet { label.text = text } } @IBInspectable var roundedCorners: Bool = false { didSet { if roundedCorners { layer.cornerRadius = 20.0 clipsToBounds = true } else { layer.cornerRadius = 0.0 clipsToBounds = false } } } @IBOutlet weak var label: UILabel! @IBOutlet weak var imageView: UIImageView!

}

一些例子:

如果我们需要在一个故事板或另一个xib中显示视图时添加更多信息,为此我们可以实现prepareForInterfaceBuilder(),此方法仅在接口生成器中打开文件时执行。 如果你做了我写的所有事情,但没有任何工作,这是一种通过在实现中添加断点来调试单个视图的方法。 这是视图层次结构。

希望这对您有所帮助,完整的示例可以在这里下载