ChatGPT解决这个技术问题 Extra ChatGPT

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

我正在尝试做一些复杂的事情,但应该是可能的。所以这里对你们所有的专家来说都是一个挑战(这个论坛有很多人:))。

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

我这样做的方式是:

创建 Question1View 对象作为 UIView 子类,定义一些 IBOutlets。创建(使用界面生成器)Question1View.xib(这里是我的问题可能在哪里)。我将 UIViewController 和 UIView 都设置为 Question1View 类。我将插座与视图的组件链接(使用 IB)。我覆盖了我的 QuestionManagerViewController 的 initWithNib 看起来像这样: - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) { / / 自定义初始化 } return self; }

当我运行代码时,我收到了这个错误:

2009-05-14 15:05:37.152 iMobiDines [17148:20b] *** 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“-[UIViewController _loadViewFromNibNamed:bundle:] 加载了“Question1View”笔尖,但视图出口没有设置。

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


a
averydev

还有一种更简单的方法来访问视图,而不是将 nib 作为数组处理。

1) 创建一个自定义 View 子类,其中包含您以后想要访问的任何插座。 - 我的观点

2) 在你想要加载和处理笔尖的 UIViewController 中,创建一个 IBOutlet 属性来保存加载的笔尖的视图,例如

在 MyViewController (一个 UIViewController 子类)

  @property (nonatomic, retain) IBOutlet UIView *myViewFromNib;

(不要忘记合成它并在你的 .m 文件中发布它)

3) 在 IB 中打开您的 nib(我们将其称为“myViewNib.xib”),将文件的所有者设置为 MyViewController

4) 现在将文件的 Owner outlet myViewFromNib 连接到 nib 中的主视图。

5) 现在在 MyViewController 中,编写以下行:

[[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil];

现在,只要你这样做,调用你的属性“self.myViewFromNib”就可以让你从你的笔尖访问视图!


它适用于主控制器视图(self.view)吗?由于某些原因,我需要在标准控制器初始化方法之后从 nib 加载主视图。原因——复杂的子类化结构
@Lukasz 你成功做到了多少?即使我也在寻找像你这样的人。
对不起,我可能很厚,但我在第一点很困惑。如何在自定义视图子类中创建网点?我以为插座只适用于 UIViewControllers?
当这个视图出现在多个父视图上的情况下呢?那么您是否建议手动为它们中的每一个编辑 xib?太糟糕了!
这仅在您想在单个视图控制器中使用自定义笔尖时才有效。如果您想在不同的视图控制器上使用它,每个控制器都动态创建自定义 nib 的实例,这将不起作用。
g
geedubb

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

使用您需要的 IBOutlets 创建您的 UIView。在 IB 中创建 xib,将其设计为您喜欢的并将其链接如下:文件的所有者属于 UIViewController 类(没有自定义子类,而是“真正的”子类)。文件所有者的视图连接到主视图,并且其类被声明为步骤 1) 中的类。将您的控件与 IBOutlets 连接起来。 DynamicViewController 可以运行它的逻辑来决定加载什么视图/xib。一旦做出决定,在 loadView 方法中输入如下内容: NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil]; QPickOneView* myView = [ nibViews objectAtIndex: 1]; myView.question = 问题;

而已!

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

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


我有一个非常相似的问题 - 我只是想从 xib 文件加载 UIView 。这些步骤对我不起作用 - 将加载的视图添加为子视图后,应用程序很快就会崩溃并出现“错误访问”。
对于 iPhone 3.0,使用 objectAtIndex:0 获取第一个元素。正如 Justicle 所描述的那样,这对我来说崩溃了。知道为什么吗?
+1 它对我来说非常有效。我想添加具有一个视图控制器的多个视图(我使用翻转视图场景,其中一部分视图旋转)我必须在数组 0 上创建索引(iPhone 3.0)
这是正确的答案,但是,应该修改代码,以便它循环通过 nibViews 并对每个对象进行类检查,这样你肯定会得到正确的对象。 objectAtIndex:1 是一个危险的假设。
Jasconius 是对的。您应该像这样遍历 nibViews 数组:gist.github.com/769539
D
Dan Rosenstark

我不确定有些答案在说什么,但我需要把这个答案放在这里,以备下次在谷歌搜索时使用。关键词:“如何从 nib 加载 UIView”或“如何从 NSBundle 加载 UIView”。

这里的代码几乎 100% 直接来自 Apress 开始 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];
} 

这假设您有一个名为 BlahUIView 子类,一个名为 Blah 的 nib,其中包含一个 UIView,其类设置为 Blah

类别:NSObject+LoadFromNib

#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

快速扩展

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

还有一个使用中的例子:

class SomeView: UIView {
    class func loadFromNib() -> SomeView? {
        return self.loadFromNib(withName: "SomeView")
    }
}

如果您使用 isKindOf,您将免受 Bundle 结构更改的影响。
assert 可能是更好的解决方案。这样你只会隐藏问题。
@Sulthan 断言本质上是不同的。我想要 Blah 类型的对象位于笔尖中的任何位置。我不在乎它是否被提升或降级。但是,为了回应您的评论,我添加了一个断言,但它not 代替了 for 循环。它补充了它。
您可以不带参数执行 loadFromNib: + (id)loadFromNib{ NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil]; for (id object in bundle) { if ([object isKindOfClass:[self class]]) { return object; } } 返回零; }
F
Francesco Vadicamo

对于所有需要管理多个自定义视图实例(即 Outlet 集合)的人,我以这种方式合并和自定义了 @Gonso、@AVeryDev 和 @Olie 答案:

创建一个自定义 MyView : UIView 并将其设置为所需 XIB 中根 UIView 的“自定义类”;在 MyView 中创建您需要的所有插座(现在就做,因为在第 3 点之后,IB 会建议您将插座连接到 UIViewController 而不是我们想要的自定义视图);将您的 UIViewController 设置为自定义视图 XIB 的“文件所有者”;在 UIViewController 中为您想要的每个 MyView 实例添加一个新的 UIViews,并将它们连接到 UIViewController 创建一个 Outlet Collection:这些视图将充当自定义视图实例的“包装”视图;最后,在 UIViewController 的 viewDidLoad 中添加以下行:

NSArray *bundleObjects;我的视图 *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;休息; } } [currView.myLabel setText:@"myText"]; [currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal]; //... [currWrapperView addSubview:currView]; [myViews addObject:currView]; } //self.myViews = myViews;如果以后需要访问它们..


嗨......你知道如何以编程方式完成这一切,即。根本不使用xib文件?
t
thgc

我会使用 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 的元素。在这种情况下,它是第一个元素。


你不是说第 2 行中的“customNib”而不是“rankingNib”吗
M
Marc W

您不应将视图控制器的类设置为 Interface Builder 中 UIView 的子类。这绝对是您问题的至少一部分。将其保留为 UIViewController、它的某个子类或您拥有的某个其他自定义类。

至于仅从 xib 加载视图,我假设您必须拥有某种视图控制器(即使它没有扩展 UIViewController,这对于您的需要)如果您想使用它来定义您的界面,请在界面构建器中设置为文件的所有者。我也做了一些研究来证实这一点。这是因为否则将无法访问 UIView 中的任何界面元素,也无法让您自己的代码方法由事件触发。

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


我正在阅读 Apress 的书“开始 iPhone 开发:探索 iPhone SDK”,他们在第 9 章:导航控制器和表格视图中讨论了这种方法。它似乎并没有太复杂。
我很想知道他们是如何做到的。我现在没有那本书的副本。
Y
Ying

你也可以使用 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 中,将 File 的 Owner 类设置为 UIViewController,并将视图类设置为 MySubView。

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


O
Olie

这是一个很好的问题(+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

将文件所有者类型设置为 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; // 太简单了:[self.view insertSubview: msView aboveSubview: mySubview]; [[mySubview superview] insertSubview: msView aboveSubview: mySubview]; // 允许嵌套 self.mySubview = msView; [oldCBView removeFromSuperview];

对我来说棘手的一点是:其他答案中的提示从 xib 加载了我的视图,但并没有替换 MyVC 中的视图(呃!)——我必须自己换掉。

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

如果有办法直接从其自己的 xib 加载 mySubview,那就太棒了,但这让我到达了我需要的地方。


我用了这个方法,谢谢。我为使其支持嵌套视图所做的一项更改是更改 [self.view insertSubview: msView aboveSubview: mySubview];到 [mySubview.superview insertSubview: msView aboveSubview: mySubview];
s
skot

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

self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];

这是该类别的代码(它与 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

k
kalafun

迅速

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

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

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


B
Brynjar

我也想做类似的事情,这就是我发现的:(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,但是当按下按钮时,我想进入一个“编辑模式”,它有一个与不同 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。这个 nib 中的 File's Owner 设置为 BViewController 还是默认的 NSObject 都没有关系,但请确保 File's Owner 中的 View Outlet 未设置为任何内容。如果是,那么我会遇到 exc_bad_access 崩溃,并且 xcode 继续加载 6677 个堆栈帧,显示重复调用的内部 UIView 方法......所以看起来像一个无限循环。 (然而,View Outlet 设置在我原来的 Bnib 中)

希望这可以帮助。


在字里行间阅读,这也帮助了我。
根据 link 中的信息,您不应该以这种方式操作视图控制器。
L
Logan

我做了一个我喜欢的类别:

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;
}

A
Andrea

对于具有可设计选项的 Swift 用户:

创建一个自定义 UIView 子类和一个 xib 文件,我们将以我们自己的类名命名:在我们的例子中是 MemeView。在 Meme View 类中,请记住在类声明之前使用 @IBDesignable 属性将其定义为可设计的 Rember 使用 Indetity Inspector 面板中的自定义 UIView 子类在 xib 中设置 File's Owner 在 xib 文件中,现在我们可以构建我们的界面,使约束,创建出口,动作等。我们需要为我们的自定义类实现一些方法来打开 xib 一旦初始化 class XibbedView: UIView { weak var nibView: UIView!覆盖便利 init(frame: CGRect) { let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last! self.init(nibName: nibName) } 需要初始化?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!让 nib = loadNib(nibName) nib.frame = bounds nib.translatesAutoresizingMaskIntoConstraints = false addSubview(nib) nibView = nib setUpConstraints() } init(nibName: String) { super.init(frame: CGRectZero) 让 nibName = NSStringFromClass(self. dynamicType).componentsSeparatedByString(".").last!让 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] 一样! UIView return view } } 在我们的自定义类中,我们还可以定义一些不可检查的属性,以便从界面构建器中完全控制它们 @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 弱变量标签:UILabel! @IBOutlet 弱 var imageView:UIImageView!

}

https://i.stack.imgur.com/wW7nG.png

https://i.stack.imgur.com/zUsxd.png

希望这有助于下载完整的示例here


完整的文章可以从我的博客 cloudintouch.it/2016/04/21/designable-xib-in-xcode-hell-yeah 中看到,但我已经发布了相关部分。
let nib = loadNib(nibName) 这是一个 XibbedView 实例,如果您调用 ``` addSubview(nib)``` 那么您是在将一个 XibbedView 实例添加到另一个 XibbedView 实例上吗?
我同意这一点。当您尝试从“调试视图层次结构”中查看它时,似乎 XibbedView 包含一个 XibbedView。你可以看到。
@WilliamHu 如果您下载提供的示例,您可以看到 MemeView.xib 只是一个带有一堆子视图的 UIView 实例,文件持有者是一个 MemeView,它是 XibbedView 的子类。因此,XibbedView 的唯一实例只是加载一个 xib 文件的 MemeView,其中主视图只是一个视图
好吧好吧。我会测试它。谢谢!
M
Meltemi

我发现 Aaron Hillegass(作者、讲师、Cocoa ninja)的this blog posting非常有启发性。即使您不采用他修改后的方法通过指定的初始化程序加载 NIB 文件,您也可能至少会更好地了解正在进行的过程。我最近一直在使用这种方法,取得了巨大的成功!


链接死了。我相信它现在位于 bignerdranch.com/blog/…
B
Barney Mattox

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

您可以使用适用于所有 2.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;

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

巴尼


Barney:“上一个答案”在 Stack Overflow 上毫无意义,因为答案会根据投票改变位置。最好参考“弗雷德的回答”或“巴尼的回答”或“奥利的回答”。
M
MusiGenesis

我有理由做同样的事情(以编程方式从 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;
}

出于一般兴趣而发布;如果有人在不这样做的情况下发现任何问题,请告诉我。


我已经实现了你的代码,但它在初始化时一直超出范围。我在 Utilities 类中创建了您的静态方法。我制作了名为 MyView(从 UIView 子类化)的 h/m 文件和同名的 xib。在 IB 中,我将 xib 文件所有者和视图设置为“MyView”。在调试器中它超出了范围。我在 IB 中错过了什么吗?
请在此处查看我的新 SO 问题:stackoverflow.com/questions/9224857/…
self = [实用程序 initWithNibName:@"XIB1" withSelf:self];当尚未设置时,您正在传递 self 。这段代码是不是和下面的一样:self = [Utilities initWithNibName:@"XIB1" withSelf:nil]; ?
@Javy:我的代码示例可能不适用于 ARC - 我没有在启用 ARC 的情况下对其进行测试。
J
Johannes

这是在 Swift 中执行此操作的一种方法(目前在 XCode 7 beta 5 中编写 Swift 2.0)。

从您在 Interface Builder 中设置为“自定义类”的 UIView 子类创建一个类似这样的方法(我的子类称为 RecordingFooterView):

class func loadFromNib() -> RecordingFooterView? {
    let nib = UINib(nibName: "RecordingFooterView", bundle: nil)
    let nibObjects = nib.instantiateWithOwner(nil, options: nil)
    if nibObjects.count > 0 {
        let topObject = nibObjects[0]
        return topObject as? RecordingFooterView
    }
    return nil
}

然后你可以这样称呼它:

let recordingFooterView = RecordingFooterView.loadFromNib()


M
Matt

@AVeryDev

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

[self.view addSubview:myViewFromNib];

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

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

提示:将加载的笔尖中主视图的不透明度设置为零,这样就不会遮挡原始笔尖中的项目。


需要注意的好事情,但是将视图添加为子视图会自动将其从其先前的父视图中删除,因此它不应该泄漏。
s
soumya

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

去做这个

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”这个词不会出现在任何地方。


A
Aamir

花了很多时间后,我伪造了以下解决方案。按照以下步骤创建自定义 UIView

https://i.stack.imgur.com/dZXFq.png

https://i.stack.imgur.com/vxSnf.png

https://i.stack.imgur.com/enKtV.png

https://i.stack.imgur.com/mGoBT.png

5) 在 myCustomView * customView 中加载 .xib。使用以下示例代码。

myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
[myView.description setText:@"description"];

注意:对于那些仍然遇到问题的人可以发表评论,我将提供示例项目的链接与 myCustomView


D
Denis Kutlubaev

最短版本:

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

n
n13

我有一个命名 xibs 的约定,其中的视图与视图相同。就像对视图控制器所做的一样。然后,我不必在代码中写出类名。我从同名的 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 {

}

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


E
Eng Yew

在 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
}

N
NANNAV

我最终为此向 UIView 添加了一个类别:

 #import "UIViewNibLoading.h"

 @implementation UIView (UIViewNibLoading)

 + (id) loadNibNamed:(NSString *) nibName {
    return [UIView loadNibNamed:nibName fromBundle:[NSBundle mainBundle] retainingObjectWithTag:1];
 }

 + (id) loadNibNamed:(NSString *) nibName fromBundle:(NSBundle *) bundle retainingObjectWithTag:(NSUInteger) tag {
    NSArray * nib = [bundle loadNibNamed:nibName owner:nil options:nil];
    if(!nib) return nil;
    UIView * target = nil;
    for(UIView * view in nib) {
        if(view.tag == tag) {
            target = [view retain];
            break;
        }
    }
    if(target && [target respondsToSelector:@selector(viewDidLoad)]) {
        [target performSelector:@selector(viewDidLoad)];
    }
    return [target autorelease];
 }

 @end

此处解释:viewcontroller is less view loading in ios&mac


人力资源管理系统。标签怎么了?
知道要保留哪个顶级对象。当时有必要,但您现在可能可以取出保留以符合 ARC。

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅