iphone - storyboard教程 - xcode下载




如何使用用Interface Builder创建的nib文件加载UIView (16)

我试图做一些细致的事情,但应该是可能的。 所以这里对你所有的专家来说都是一个挑战(这个论坛是你们中许多人的一部分:))。

我创建了一个调查问卷“组件”,我想在NavigationContoller (我的QuestionManagerViewController )上加载它。 “组件”是一个“空的” UIViewController ,它可以根据需要回答的问题加载不同的视图。

我这样做的方式是:

  1. 创建Question1View对象作为UIView子类,定义一些IBOutlets
  2. 创建(使用Interface Builder) Question1View.xib (这里是我的问题可能是 )。 我将UIViewControllerUIView都设置为Question1View类。
  3. 我将插座与视图的组件(使用IB)相链接。
  4. 我重写了我的QuestionManagerViewControllerinitWithNib ,如下所示:

    - (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:]加载了“Question1View”笔尖,但视图插座没有设置。“

我确定有一种方法可以使用nib文件加载视图,而无需创建viewController类。


@AVeryDev

6)将加载的视图附加到视图控制器的视图:

[self.view addSubview:myViewFromNib];

据推测,有必要从视图中删除它以避免内存泄漏。

为了澄清:视图控制器有几个IBOutlets,其中一些连接到原始笔尖文件中的项目(如往常一样),另一些连接到加载笔尖中的项目。 两个笔尖都拥有相同的所有者类别。 加载的视图覆盖原始视图。

提示:将装入的笔尖中的主视图的不透明度设置为零,然后它不会遮掩原始笔尖上的项目。


迅速

其实我对这个问题的解决方法是,在viewDidLoad中加载视图,在我的CustonViewController中,我想使用这样的视图:

myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView

不要在loadView()方法中加载视图! loadView方法用于为您的自定义ViewController加载视图。


以前的答案没有考虑到在iPhone SDK的2.0和2.1之间发生的NIB(XIB)结构的变化。 用户内容现在从索引0开始,而不是1。

您可以使用2.1版本以上的所有版本(在IPHONE之前有两个下划线:

 // Cited from previous example
 NSArray* nibViews =  [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil];
 int startIndex;
 #ifdef __IPHONE_2_1
 startIndex = 0;
 #else
 startIndex = 1;
 #endif
 QPickOneView* myView = [ nibViews objectAtIndex: startIndex];
 myView.question = question;

我们对大多数应用程序使用与此类似的技术。

巴尼


你也可以使用UIViewController的initWithNibName而不是loadNibNamed。 我觉得这很简单。

UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil];
[self.subview addSubview:aViewController.view];
[aViewController release];  // release the VC

现在你只需要创建MySubView.xib和MySubView.h / m。 在MySubView.xib中,将文件的所有者类设置为UIViewController并将类视为MySubView。

您可以使用父级xib文件定位subview大小和位置。


对于所有需要管理自定义视图的多个实例的那些,这是一个Outlet集合 ,我以这种方式合并和自定义@Gonso,@AVeryDev和@Olie的答案:

  1. 创建一个自定义MyView : UIView ,并将其设置为所需XIB中的根UIViewCustom Class ”;

  2. MyView创建你需要的所有插座 (现在这样做,因为在第3点之后,IB会建议你将插座连接到UIViewController而不是我们想要的自定义视图);

  3. 将你的UIViewController设置为自定义视图XIB的文件所有者

  4. UIViewController为每个你想要的MyView实例添加一个新的UIViews ,并将它们连接到UIViewController创建一个Outlet集合 :这些视图将作为自定义视图实例的“包装器”视图;

  5. 最后,在UIViewControllerviewDidLoad中添加以下几行:

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..

您不应该将视图控制器的类设置为Interface Builder中的UIView的子类。 这绝对是你问题的一部分。 留下它作为UIViewController ,它的一些子类,或者你有的其他自定义类。

至于从xib载入一个视图,我假设你必须拥有某种视图控制器(即使它没有扩展UIViewController ,它可能对你的需要来说太重了)作为文件的所有者来设置Interface Builder如果你想用它来定义你的界面。 我做了一些研究来证实这一点。 这是因为否则就无法访问UIView中的任何接口元素,也没有办法让事件触发代码中的自己的方法。

如果您将UIViewController用作视图的文件所有者,则可以使用initWithNibName:bundle:来加载它并获取视图控制器对象。 在IB中,确保使用xib中的界面将view插座设置为view 。 如果您使用其他类型的对象作为文件所有者,则需要使用NSBundleloadNibNamed:owner:options:方法来加载nib,并将文件所有者的实例传递给该方法。 其所有属性将根据您在IB中定义的网点正确设置。


我也想做类似的事情,这是我发现的:(SDK 3.1.3)

我有一个视图控制器A(本身由Nav控制器拥有),它可以通过按下按钮加载VC B:

在AViewController.m中

BViewController *bController = [[BViewController alloc] initWithNibName:@"Bnib" bundle:nil];
[self.navigationController pushViewController:bController animated:YES];
[bController release];

现在VC B有它的Bnib接口,但是当按下一个按钮时,我想要进入一个'编辑模式',它有一个不同于另一个nib的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];

然后在另一个按钮上按(退出编辑模式):

[editView removeFromSuperview];

我回到了我最初的Bnib。

这工作正常,但请注意我的EditMode.nib只有1个顶级obj,它是一个UIView obj。 无论这个笔尖中的文件所有者是否设置为BViewController或默认NSObject,但要确保文件所有者中的View Outlet未设置为任何内容。 如果是,那么我得到一个exc_bad_access崩溃,xcode继续加载6677堆栈帧,显示内部UIView方法重复调用...所以看起来像一个无限循环。 (然而,View Outlet IS是在我原来的Bnib中设置的)

希望这可以帮助。


我会使用UINib来实例化一个自定义的UIView来重用

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

在这种情况下需要的文件是MyCustomView.xibMyCustomViewClass.hMyCustomViewClass.m请注意, [UINib instantiateWithOwner]返回一个数组,因此您应该使用反映您想要重用的UIView的元素。 在这种情况下,它是第一个元素。


我发现Aaron Hillegass(作者,导师,Cocoa ninja)的这篇博文非常有启发性。 即使你没有采用他的修改方法通过指定的初始化程序来加载NIB文件,你至少可以更好地理解正在发生的过程。 最近我一直在使用这种方法取得巨大成功!


我有一个命名xib的惯例,它们的视图与视图相同。 和人们对视图控制器所做的一样。 然后,我不必在代码中写出类名。 我从具有相同名称的nib文件加载UIView。

一个名为MyView的类的示例。

  • 在Interface Builder中创建一个名为MyView.xib的nib文件
  • 在Interface Builder中,添加一个UIView。 将其类设置为MyView。 自定义您的内容,将MyView的实例变量连接到您可能希望稍后访问的子视图。
  • 在你的代码中,像这样创建一个新的MyView:

    MyView * myView = [MyView nib_viewFromNibWithOwner: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 {

}

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


最短版本:

RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];

没有一个答案解释了如何创建这个问题的根源的独立XIB。 没有Xcode 4选项来“创建新的XIB文件”。

去做这个

1) Choose "New File..."
2) Choose the "User Interface" category under the iOS section
3) Choose the "View" item
4) You will then be prompted to choose an iPhone or iPad format

这看起来很简单,但它可以节省你几分钟的时间,因为“XIB”这个词不会出现在任何地方。


要以编程方式在Swift 4中从nib / xib加载视图,请执行以下操作:

// Load a view from a Nib given a placeholder view subclass
//      Placeholder is an instance of the view to load.  Placeholder is discarded.
//      If no name is provided, the Nib name is the same as the subclass type name
//
public func loadViewFromNib<T>(placeholder placeholderView: T, name givenNibName: String? = nil) -> T {

    let nib = loadNib(givenNibName, placeholder: placeholderView)
    return instantiateView(fromNib: nib, placeholder: placeholderView)
}

// Step 1: Returns a Nib
//
public func loadNib<T>(_ givenNibName: String? = nil, placeholder placeholderView: T) -> UINib {
    //1. Load and unarchive nib file
    let nibName = givenNibName ?? String(describing: type(of: placeholderView))

    let nib = UINib(nibName: nibName, bundle: Bundle.main)
    return nib
}

// Step 2: Instantiate a view given a nib
//
public func instantiateView<T>(fromNib nib: UINib, placeholder placeholderView: T) -> T {
    //1. Get top level objects
    let topLevelObjects = nib.instantiate(withOwner: placeholderView, options: nil)

    //2. Have at least one top level object
    guard let firstObject = topLevelObjects.first else {
        fatalError("\(#function): no top level objects in nib")
    }

    //3. Return instantiated view, placeholderView is not used
    let instantiatedView = firstObject as! T
    return instantiatedView
}

谢谢你们。 我确实找到了一种做我想做的事情的方式。

  1. 用你需要的IBOutlet创建你的UIView
  2. 在IB中创建xib,按照喜欢的方式设计它,并像下面这样链接它:文件的所有者是类UIViewController (没有自定义子类,但是是“真正的”子类)。 文件所有者的视图连接到主视图,并将其类声明为步骤1)中的一个。
  3. 将您的控件与IBOutlet连接起来。
  4. DynamicViewController可以运行其逻辑来决定要加载的视图/ xib。 一旦它做出决定,在loadView方法中就像这样:

    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
                                                      owner:self
                                                    options:nil];
    
    QPickOneView* myView = [ nibViews objectAtIndex: 1];
    
    myView.question = question;
    

而已!

主包的loadNibNamed方法将负责初始化视图并创建连接。

现在ViewController可以根据内存中的数据显示一个视图或另一个视图,并且“父”屏幕不需要打扰这个逻辑。


这是一个很好的问题(+1),答案几乎很有帮助;)对不起,伙计们,但我有一段时间都在琢磨着,尽管Gonso和AVeryDev都给出了很好的提示。 希望这个答案能够帮助其他人。

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

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

  • 在MyVC.xib中,创建一个类型为MySubView的视图,该视图具有正确的大小和形状,并定位在您想要的位置。
  • 在MyVC.h中,有

    IBOutlet MySubview *mySubView
    // ...
    @property (nonatomic, retain) MySubview *mySubview;
    
  • 在MyVC.m中, @synthesize mySubView; 并且不要忘记在dealloc释放它。

  • 在MySubview.h中,有一个用于UIView *view的出口/属性(可能是不必要的,但为我工作。)在.m中合成并释放它
  • 在MySubview.xib中
    • 将文件所有者类型设置为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];
    

对我来说,棘手的一点是:其他答案中的提示从xib加载了我的视图,但并未替换MyVC中的视图(杜!) - 我不得不自己交换视图。

此外,要访问mySubview的方法,必须将MySubview文件中的view属性设置为MySubview 。 否则,它会作为一个普通的UIView回来。

如果有一种方法可以直接从它自己的xib载入mySubview,那会很容易,但是这让我在需要的地方。


这是应该更容易的事情。 我最终扩展了UIViewController并添加了一个loadNib:inPlaceholder:选择器。 现在我可以说

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




interface-builder