我试着做一些复杂的事情,但应该是可能的。所以这里有一个对你们所有专家的挑战(这个论坛是由你们很多人组成的:))。
我正在创建一个问卷“组件”,我想加载在一个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类。
这是一件应该比较容易的事情。我最终扩展了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
还有一种更简单的方法来访问视图,而不是将nib作为数组处理。
1)创建一个自定义的View子类,其中包含你以后想要访问的任何出口。——MyView
2)在你想要加载和处理nib的UIViewController中,创建一个IBOutlet属性来保存加载的nib的视图
在MyViewController中(一个UIViewController子类)
@property (nonatomic, retain) IBOutlet UIView *myViewFromNib;
(别忘了合成它并在你的。m文件中发布它)
3)在IB中打开你的nib(我们称之为'myViewNib.xib'),设置你文件的所有者为MyViewController
4)现在连接你的文件的所有者出口myViewFromNib到nib的主视图。
5)现在在MyViewController中,写以下一行:
[[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil];
当你这样做的时候,把你的财产称为“自我”。myViewFromNib”将让你从你的nib访问视图!
这是一个很好的问题(+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,那将摇滚,但这让我在我需要的地方。
我做了一个我喜欢的分类:
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;
}