我试着做一些复杂的事情,但应该是可能的。所以这里有一个对你们所有专家的挑战(这个论坛是由你们很多人组成的:))。
我正在创建一个问卷“组件”,我想加载在一个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
谢谢大家。
我确实找到了做我想做的事的方法。
Create your UIView with the IBOutlets you need.
Create the xib in IB, design it to you liking and link it like this: The File's Owner is of class UIViewController (No custom subclass, but the "real" one). The File Owner's view is connected to the main view and its class is declared as the one from step 1).
Connect your controls with the IBOutlets.
The DynamicViewController can run its logic to decide what view/xib to load. Once its made the decission, in the loadView method put something like this:
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
owner:self
options:nil];
QPickOneView* myView = [ nibViews objectAtIndex: 1];
myView.question = question;
就是这样!
主bundle的loadNibNamed方法将负责初始化视图和创建连接。
现在ViewController可以根据内存中的数据显示一个视图或另一个视图,而“父”屏幕不需要为这个逻辑而烦恼。
我有一种命名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在标签上设置内容。
我有理由做同样的事情(从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作为数组处理。
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访问视图!