我试着做一些复杂的事情,但应该是可能的。所以这里有一个对你们所有专家的挑战(这个论坛是由你们很多人组成的:))。
我正在创建一个问卷“组件”,我想加载在一个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类。
我也想做一些类似的事情,这是我发现的:
(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然而)
希望这能有所帮助。
这是一个很好的问题(+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;
}
这是一件应该比较容易的事情。我最终扩展了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