239 iphone cocoa-touch objective-c interface-builder ios
我正在尝试做一些精心设计的事情,但这应该是可行的.所以这里对你所有的专家都是一个挑战(这个论坛是你们很多人的一部分:)).
我正在创建一个问卷调查"组件",我想在NavigationContoller(我的QuestionManagerViewController)上加载."组件"是"空" UIViewController,可以根据需要回答的问题加载不同的视图.
我这样做的方式是:
UIView子类,定义一些IBOutlets.Question1View.xib (这里可能是我的问题所在).我同时设置UIViewController和UIView为类Question1View的.我重写initWithNib我QuestionManagerViewController的看起来像这样:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
Run Code Online (Sandbox Code Playgroud)当我运行代码时,我收到此错误:
2009-05-14 15:05:37.152 iMobiDines [17148:20b]***因未捕获的异常终止应用程序'
NSInternalInconsistencyException',原因:'-[UIViewController _loadViewFromNibNamed:bundle:]加载了'Question1View"笔尖,但未设置视图插座."
我确信有一种方法可以使用nib文件加载视图,而无需创建viewController类.
ave*_*dev 224
还有一种更简单的方法来访问视图,而不是将nib作为数组处理.
1)创建一个自定义View子类,其中包含您希望以后可以访问的任何出口. - 我的看法
2)在你想要加载和处理nib的UIViewController中,创建一个IBOutlet属性来保存加载的nib的视图,例如
在MyViewController中(一个UIViewController子类)
@property (nonatomic, retain) IBOutlet UIView *myViewFromNib;
Run Code Online (Sandbox Code Playgroud)
(别忘了合成它并在你的.m文件中发布它)
3)在IB中打开你的笔尖(我们称之为'myViewNib.xib'),将你文件的所有者设置为MyViewController
4)现在将文件的所有者插座myViewFromNib连接到笔尖中的主视图.
5)现在在MyViewController中,写下以下行:
[[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil];
Run Code Online (Sandbox Code Playgroud)
现在,只要您这样做,调用您的属性"self.myViewFromNib"将允许您从笔尖访问视图!
小智 120
谢谢你们.我确实找到了一种方法来做我想做的事.
UIView用IBOutlet你需要的东西创造你的.UIViewController(没有自定义子类,但是"真正的"子类).文件所有者的视图连接到主视图,其类声明为步骤1)中的类.IBOutlets 连接.在DynamicViewController可以运行它的逻辑来决定哪些视图/厦门国际银行加载.一旦它做出了决定,在loadView方法中放了这样的东西:
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
owner:self
options:nil];
QPickOneView* myView = [ nibViews objectAtIndex: 1];
myView.question = question;
Run Code Online (Sandbox Code Playgroud)而已!
主bundle的loadNibNamed方法将负责初始化视图和创建连接.
现在,ViewController可以根据内存中的数据显示一个或另一个视图,而"父"屏幕不需要打扰这个逻辑.
Dan*_*ark 69
我不确定一些答案是在谈论什么,但我需要在下次搜索谷歌时提出这个答案.关键字:"如何从笔尖加载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];
}
Run Code Online (Sandbox Code Playgroud)
这假设您有一个UIView名为的子类Blah,一个名为nib 的子类,Blah其中包含一个UIView其类设置为的子类Blah.
#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
Run Code Online (Sandbox Code Playgroud)
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
}
}
Run Code Online (Sandbox Code Playgroud)
并使用一个例子:
class SomeView: UIView {
class func loadFromNib() -> SomeView? {
return self.loadFromNib(withName: "SomeView")
}
}
Run Code Online (Sandbox Code Playgroud)
Fra*_*amo 22
对于那些需要管理自定义视图的多个实例(即Outlet Collection)的人,我以这种方式合并并自定义@Gonso,@ AveryDev和@Olie答案:
创建一个自定义MyView : UIView并将其设置为所需XIB中根的" 自定义类 " ;
UIView
创建你需要的所有出口MyView(现在就去做,因为在第3点之后,IB会建议你将出口连接到UIViewController而不是我们想要的自定义视图);

将您设置UIViewController为自定义视图XIB的 " 文件所有者 " ;

在为您想要的每个实例UIViewController添加一个新UIViews的MyView,并将它们连接到UIViewController创建Outlet集合:这些视图将充当自定义视图实例的"包装"视图;

最后,在viewDidLoad您UIViewController添加以下行:
Run Code Online (Sandbox Code Playgroud)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..
thg*_*hgc 18
我会使用UINib来实例化一个可以重用的自定义UIView
UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil];
MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0];
[self.view addSubview:customView];
Run Code Online (Sandbox Code Playgroud)
在这种情况下所需的文件是MyCustomView.xib,MyCustomViewClass.h和MyCustomViewClass.m
注意[UINib instantiateWithOwner]返回一个数组,因此您应该使用反映您想要重用的UIView的元素.在这种情况下,它是第一个元素.
Mar*_*c W 11
您不应该将视图控制器的类设置UIView为Interface Builder中的子类.这绝对是你问题的至少一部分.将其保留为其中的UIViewController某个子类,或其他一些自定义类.
至于仅从xib加载视图,我假设您必须拥有某种视图控制器(即使它没有扩展UIViewController,这可能对您的需求来说太重量级)设置为接口中的文件所有者如果您想使用它来定义您的界面,请使用Builder.我做了一些研究来证实这一点.这是因为否则将无法访问其中的任何界面元素UIView,也无法通过事件触发代码中自己的方法.
如果您使用a UIViewController作为视图的文件所有者,则可以使用initWithNibName:bundle:它来加载它并获取视图控制器对象.在IB中,确保view使用xib中的界面将插座设置为视图.如果你使用的是其他类型的对象作为文件的拥有者,你需要使用NSBundle的loadNibNamed:owner:options:方法加载笔尖,通过文件拥有者的一个实例的方法.其所有属性将根据您在IB中定义的出口进行正确设置.
小智 10
您还可以使用UIViewController的initWithNibName而不是loadNibNamed.我发现它更简单.
UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil];
[self.subview addSubview:aViewController.view];
[aViewController release]; // release the VC
Run Code Online (Sandbox Code Playgroud)
现在您只需要创建MySubView.xib和MySubView.h/m.在MySubView.xib中,将File的Owner类设置为UIViewController,并将类查看为MySubView.
您可以subview使用父xib文件定位和调整大小.
这是一个很好的问题(+1)并且答案几乎是有帮助的;)对不起,伙计们,但我有一点时间在这里,虽然Gonso和AVeryDev提供了很好的提示.希望这个答案可以帮助别人.
MyVC 是持有所有这些东西的视图控制器.
MySubview 是我们想要从xib加载的视图
MySubView合适的视图,并将其放置在您想要的位置.在MyVC.h中,有
IBOutlet MySubview *mySubView
// ...
@property (nonatomic, retain) MySubview *mySubview;
Run Code Online (Sandbox Code Playgroud)在MyVC.m中,@synthesize mySubView;不要忘记将其发布dealloc.
UIView *view(可能没必要,但为我工作.)在.m中合成并释放它MySubview,并将视图属性链接到您的视图.IBOutlet根据需要连接到回到MyVC.m,有
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];
Run Code Online (Sandbox Code Playgroud)对我来说棘手的一点是:其他答案中的提示从xib加载了我的视图,但没有替换MyVC中的视图(呃!) - 我不得不自己交换它.
此外,要访问mySubview方法,view必须将.xib文件中的属性设置为MySubview.否则,它会以一个普通的方式回归UIView.
如果有一种方法可以mySubview直接从它自己的xib加载,那就是摇滚,但这让我得到了我需要的位置.
这应该是更容易的事情.我最终扩展UIViewController并添加了一个loadNib:inPlaceholder:选择器.现在我可以说
self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];
Run Code Online (Sandbox Code Playgroud)
这是该类别的代码(它与Gonso描述的具有相同的rigamarole):
@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
Run Code Online (Sandbox Code Playgroud)
我也想做类似的事情,这就是我发现的:(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];
Run Code Online (Sandbox Code Playgroud)
现在VC B的界面来自Bnib,但是当按下一个按钮时,我想进入一个"编辑模式",它有一个与不同笔尖分开的UI,但是我不希望新的VC用于编辑模式,我希望新的笔尖与我现有的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];
Run Code Online (Sandbox Code Playgroud)
然后按另一个按钮(退出编辑模式):
[editView removeFromSuperview];
Run Code Online (Sandbox Code Playgroud)
我回到原来的Bnib.
这工作正常,但请注意我的EditMode.nib中只有一个顶级obj,一个UIView obj.此笔尖中的文件所有者是否设置为BViewController或默认NSObject并不重要,但请确保文件所有者中的视图插座未设置为任何内容.如果是,那么我得到一个exc_bad_access崩溃,xcode继续加载6677堆栈帧,显示一个内部UIView方法重复调用...所以看起来像一个无限循环.(但是,我的原始Bnib中设置了View Outlet)
希望这可以帮助.
我做了一个我喜欢的类别:
UIView+NibInitializer.h
#import <UIKit/UIKit.h>
@interface UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil;
@end
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
然后,这样打电话:
MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil];
Run Code Online (Sandbox Code Playgroud)
如果您的笔尖的名称不是您班级的名称,请使用笔尖名称.
要在子类中覆盖它以获取其他行为,它可能如下所示:
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
self = [super initWithNibNamed:nibNameOrNil];
if (self) {
self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
}
return self;
}
Run Code Online (Sandbox Code Playgroud)
在迅速
实际上我对这个问题的解决方法是,在我的CustonViewController中的viewDidLoad中加载视图,我想在那里使用视图:
myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView
Run Code Online (Sandbox Code Playgroud)
不要在loadView()方法中加载视图!loadView方法用于加载自定义ViewController的视图.
对于具有可选项的Swift用户:
UIView子类和一个xib文件,我们将以我们自己的类名命名:在我们的例子MemeView中。在Meme View类中,请记住@IBDesignable在类声明之前使用属性将其定义为可设计的通过UIViewIndetity Inspector面板中的自定义子类,Rember可以在xib中设置文件的所有者
我们需要为自定义类实现一些方法以在初始化后打开xib
class XibbedView: UIView {
weak var nibView: UIView!
override convenience init(frame: CGRect) {
let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
self.init(nibName: nibName)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
let nib = loadNib(nibName)
nib.frame = bounds
nib.translatesAutoresizingMaskIntoConstraints = false
addSubview(nib)
nibView = nib
setUpConstraints()
}
init(nibName: String) {
super.init(frame: CGRectZero)
let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
let nib = loadNib(nibName)
nib.frame = bounds
nib.translatesAutoresizingMaskIntoConstraints = false
addSubview(nib)
nibView = nib
setUpConstraints()
}
func setUpConstraints() {
["V","H"].forEach { (quote) -> () in
let format = String(format:"\(quote):|[nibView]|")
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: [], metrics: nil, views: ["nibView" : nibView]))
}
}
func loadNib(name: String) -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: name, bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
Run Code Online (Sandbox Code Playgroud)
}
在我们的自定义类中,我们还可以定义一些无法检查的属性,以从界面生成器完全控制它们
@IBDesignable
class MemeView: XibbedView {
@IBInspectable var memeImage: UIImage = UIImage() {
didSet {
imageView.image = memeImage
}
}
@IBInspectable var textColor: UIColor = UIColor.whiteColor() {
didSet {
label.textColor = textColor
}
}
@IBInspectable var text: String = "" {
didSet {
label.text = text
}
}
@IBInspectable var roundedCorners: Bool = false {
didSet {
if roundedCorners {
layer.cornerRadius = 20.0
clipsToBounds = true
}
else {
layer.cornerRadius = 0.0
clipsToBounds = false
}
}
}
@IBOutlet weak var label: UILabel!
@IBOutlet weak var imageView: UIImageView!
Run Code Online (Sandbox Code Playgroud)}
如果我们需要添加更多信息,而视图是在情节提要板或其他xib中显示的,则可以实现prepareForInterfaceBuilder()此方法,只有在界面构建器中打开文件时,才执行此方法。如果您做了我写的所有事情,但没有任何效果,这是通过在实现中添加断点来调试sigle视图的一种方法。

这是视图层次结构。

希望可以帮助您在此处下载完整示例