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

我正在创建一个问卷“组件”,我想加载在一个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类。


当前回答

没有一个答案解释如何创建独立的XIB,这是这个问题的根源。Xcode 4没有“创建新的XIB文件”选项。

要做到这一点

1) Choose "New File..."
2) Choose the "User Interface" category under the iOS section
3) Choose the "View" item
4) You will then be prompted to choose an iPhone or iPad format

这看起来很简单,但它可以为您节省几分钟的时间,因为“XIB”这个词没有出现在任何地方。

其他回答

我有理由做同样的事情(从XIB文件编程加载一个视图),但我需要完全从一个UIView的子类的子类的上下文(即不涉及视图控制器的任何方式)。为了做到这一点,我创建了这个实用方法:

+ (id) initWithNibName:(NSString *)nibName withSelf:(id)myself {

    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName
                                                    owner:myself options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:[myself class]]) {
            return object;
        }
    }  

    return nil;
} 

然后我从我的子类的initWithFrame方法调用它,如下所示:

- (id)initWithFrame:(CGRect)frame {

    self = [Utilities initWithNibName:@"XIB1" withSelf:self];
    if (self) {
        // Initialization code.
    }
    return self;
}

张贴的一般利益;如果有人发现没有这样做的问题,请告诉我。

我不确定一些答案在谈论什么,但我需要把这个答案放在这里,当我下次在谷歌中搜索时。关键词:“如何从一个nib加载一个UIView”或“如何从一个NSBundle加载一个UIView”

下面的代码几乎100%直接来自Apress Beginning iPhone 3的书籍(第247页,“使用新的表视图单元格”):

- (void)viewDidLoad {
    [super viewDidLoad];
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"Blah"
                                                 owner:self options:nil];
    Blah *blah;
    for (id object in bundle) {
        if ([object isKindOfClass:[Blah class]]) {
            blah = (Blah *)object;
            break;
        }
    }   
    assert(blah != nil && "blah can't be nil");
    [self.view addSubview: blah];
} 

这假设你有一个UIView子类叫Blah,一个nib叫Blah它包含一个UIView它的类设置为Blah。


类别:+ LoadFromNib NSObject

#import "NSObject+LoadFromNib.h"

@implementation NSObject (LoadFromNib)

+ (id)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad {
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:classToLoad]) {
            return object;
        }
    }
    return nil;
}

@end

迅速扩展

extension UIView {
    class func loadFromNib<T>(withName nibName: String) -> T? {
        let nib  = UINib.init(nibName: nibName, bundle: nil)
        let nibObjects = nib.instantiate(withOwner: nil, options: nil)
        for object in nibObjects {
            if let result = object as? T {
                return result
            }
        }
        return nil
    }
}

还有一个正在使用的例子:

class SomeView: UIView {
    class func loadFromNib() -> SomeView? {
        return self.loadFromNib(withName: "SomeView")
    }
}

对于那些需要管理多个自定义视图实例的人,也就是Outlet Collection,我合并并定制了@Gonso, @AVeryDev和@Olie的答案:

Create a custom MyView : UIView and set it as "Custom Class" of the root UIView in the desired XIB; Create all outlets you need in MyView (do it now because after point 3 the IB will propose you to connect outlets to the UIViewController and not to the custom view as we want); Set your UIViewController as "File's Owner" of the custom view XIB; In the UIViewController add a new UIViews for each instance of MyView you want, and connect them to UIViewController creating an Outlet Collection: these views will act as "wrapper" views for the custom view instances; Finally, in the viewDidLoad of your UIViewController add the following lines:

NSArray *bundleObjects; MyView *currView; NSMutableArray *myViews = [NSMutableArray arrayWithCapacity:myWrapperViews.count]; for (UIView *currWrapperView in myWrapperViews) { bundleObjects = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil]; for (id object in bundleObjects) { if ([object isKindOfClass:[MyView class]]){ currView = (MyView *)object; break; } } [currView.myLabel setText:@"myText"]; [currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal]; //... [currWrapperView addSubview:currView]; [myViews addObject:currView]; } //self.myViews = myViews; if need to access them later..

我最终为这个添加了一个类别到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是更少的视图加载

这是一件应该比较容易的事情。我最终扩展了UIViewController并添加了一个loadNib:inPlaceholder: selector。现在我可以说

self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];

下面是这个类别的代码(它和Gonso描述的一样):

@interface UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName;
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder;

@end

@implementation UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName
{
  NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]; 
  for (id view in xib) { // have to iterate; index varies
    if ([view isKindOfClass:[UIView class]]) return view;
  }
  return nil;
}

- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
  UIView *nibView = [self viewFromNib:nibName];
  [nibView setFrame:placeholder.frame];
  [self.view insertSubview:nibView aboveSubview:placeholder];
  [placeholder removeFromSuperview];
  return nibView;
}

@end