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

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


当前回答

我将使用UINib实例化一个自定义的UIView被重用

UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil];
MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0];
[self.view addSubview:customView];

本例中需要的文件是MyCustomView。mycustomviewclass . xib、MyCustomViewClass.h和MyCustomViewClass.m 注意,[UINib instantiatewiowner]返回一个数组,所以你应该使用反映你想要重用的UIView的元素。在这个例子中,它是第一个元素。

其他回答

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

对于那些需要管理多个自定义视图实例的人,也就是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..

这是一件应该比较容易的事情。我最终扩展了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

这是一个很好的问题(+1),答案几乎是有帮助的;)抱歉,伙计们,虽然Gonso和AVeryDev都给出了很好的提示,但我花了很长时间才读完这篇文章。希望这个答案能帮助到其他人。

MyVC是持有这些东西的视图控制器。

MySubview是我们想从xib中加载的视图

In MyVC.xib, create a view of type MySubView that is the right size & shape & positioned where you want it. In MyVC.h, have IBOutlet MySubview *mySubView // ... @property (nonatomic, retain) MySubview *mySubview; In MyVC.m, @synthesize mySubView; and don't forget to release it in dealloc. In MySubview.h, have an outlet/property for UIView *view (may be unnecessary, but worked for me.) Synthesize & release it in .m In MySubview.xib set file owner type to MySubview, and link the view property to your view. Lay out all the bits & connect to the IBOutlet's as desired Back in MyVC.m, have NSArray *xibviews = [[NSBundle mainBundle] loadNibNamed: @"MySubview" owner: mySubview options: NULL]; MySubview *msView = [xibviews objectAtIndex: 0]; msView.frame = mySubview.frame; UIView *oldView = mySubview; // Too simple: [self.view insertSubview: msView aboveSubview: mySubview]; [[mySubview superview] insertSubview: msView aboveSubview: mySubview]; // allows nesting self.mySubview = msView; [oldCBView removeFromSuperview];

对我来说棘手的一点是:其他答案中的提示从xib加载了我的视图,但没有替换MyVC中的视图(呸!)——我必须自己交换。

同样,为了访问mySubview的方法,.xib文件中的view属性必须设置为mySubview。否则,它会返回一个普通的UIView。

如果有一种方法来加载mySubview直接从它自己的xib,那将摇滚,但这让我在我需要的地方。

我有一种命名xib的约定,其中的视图与视图相同。和视图控制器一样。这样,我就不必在代码中写出类名了。我从同名的nib文件中加载一个UIView。

一个名为MyView的类的例子。

创建一个名为MyView的nib文件。接口生成器中的xib 在Interface Builder中添加一个UIView。将其类设置为MyView。自定义到你的内心的内容,连接MyView的实例变量到子视图,你可能想以后访问。 在你的代码中,像这样创建一个新的MyView: MyView * MyView = [MyView nib_viewfromnibwiowner:owner];

这是它的分类:

@implementation UIView (nib)

+ (id) nib_viewFromNib {
    return [self nib_viewFromNibWithOwner:nil];
}

+ (id) nib_viewFromNibWithOwner:(id)owner {
    NSString *className = NSStringFromClass([self class]);
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:className owner:owner options:nil];
    UIView *view = nil;
    for(UIView *v in nib) {
        if ([v isKindOfClass:[self class]]) {
            view = v;
            break;
        }
    }
    assert(view != nil && "View for class not found in nib file");
    [view nib_viewDidLoad];
    return view;
}

// override this to do custom setup
-(void)nib_viewDidLoad {

}

然后,我将连接按钮与我正在使用的控制器的动作,并使用自定义视图子类中的outlet在标签上设置内容。