iOS loadNibNamed 混淆,最佳实践是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13534502/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 21:18:55  来源:igfitidea点击:

iOS loadNibNamed confusion, what is best practice?

iosuiviewinitiboutletloadnibnamed

提问by Cloov

I'm familiar with most of the process of creating an XIB for my own UIView subclass, but not everything is working properly for me - it's mostly to do with the IBOutlets linking up. I can get them to work in what seems like a roundabout way.

我熟悉为我自己的 UIView 子类创建 XIB 的大部分过程,但并非一切对我来说都正常工作 - 这主要与 IBOutlets 链接有关。我可以让他们以一种看似迂回的方式工作。

My setup is this:

我的设置是这样的:

  • I have MyClass.h and MyClass.m. They have IBOutlets for a UIView (called view) and a UILabel (called myLabel). I added the 'view' property because some examples online seemed to suggest that you need this, and it actually solved an issue where I was getting a crash because it couldn't find the view property, I guess not even in the UIView parent class.
  • I have an XIB file called MyClass.xib, and its File's Owner custom class is MyClass, which prefilled correctly after my .h and .m for that class existed.
  • 我有 MyClass.h 和 MyClass.m。他们有一个 UIView(称为视图)和一个 UILabel(称为 myLabel)的 IBOutlets。我添加了 'view' 属性,因为网上的一些例子似乎表明你需要这个,它实际上解决了我因为找不到 view 属性而崩溃的问题,我猜甚至在 UIView 父类中也没有.
  • 我有一个名为 MyClass.xib 的 XIB 文件,它的 File's Owner 自定义类是 MyClass,它在该类的 .h 和 .m 存在后正确预填充。

My init method is where I'm having issues.

我的 init 方法是我遇到问题的地方。

I tried to use the NSBundle mainBundle's 'loadNibNamed' method and set the owner to 'self', hoping that I'd be creating an instance of the view and it'd automatically get its outlets matched to the ones in my class (I know how to do this and I'm careful with it). I then thought I'd want to make 'self' equal to the subview at index 0 in that nib, rather than doing

我尝试使用 NSBundle mainBundle 的“loadNibNamed”方法并将所有者设置为“self”,希望我能够创建视图的一个实例,并且它会自动将其出口与我班级中的出口相匹配(我知道如何做到这一点,我很小心)。然后我想我想让 'self' 等于那个笔尖中索引 0 处的子视图,而不是做

self = [super init];

or anything like that.

或类似的东西。

I sense that I'm doing things wrong here, but examples online have had similar things going on in the init method, but they assign that subview 0 to the view property and add it as a child - but is that not then a total of two MyClass instances? One essentially unlinked to IBOutlets, containing the child MyClass instantiated via loadNibNamed? Or at best, is it not a MyClass instance with an extra intermediary UIView containing all the IBOutlets I originally wanted as direct children of MyClass? That poses a slight annoyance when it comes to doing things like instanceOfMyClass.frame.size.width, as it returns 0, when the child UIView that's been introduced returns the real frame size I was looking for.

我觉得我在这里做错了,但是在线示例在 init 方法中发生了类似的事情,但是他们将该子视图 0 分配给视图属性并将其添加为子视图 - 但那不是总共两个 MyClass 实例?一个基本上没有链接到 IBOutlets,包含通过 loadNibNamed 实例化的子 MyClass?或者充其量,它不是一个带有额外中间 UIView 的 MyClass 实例,其中包含我最初想要作为 MyClass 的直接子级的所有 IBOutlets?当涉及到执行诸如 instanceOfMyClass.frame.size.width 之类的事情时,这会带来一些烦恼,因为它返回 0,当引入的子 UIView 返回我正在寻找的真实帧大小时。

Is the thing I'm doing wrong that I'm messing with loadNibNamed inside an init method? Should I be doing something more like this?

我在 init 方法中弄乱了 loadNibNamed 是我做错了吗?我应该做更多这样的事情吗?

MyClass *instance = [[MyClass alloc] init];
[[NSBundle mainBundle] loadNibNamed:@"MyClass" owner:instance options:nil];  

Or like this?

或者像这样?

MyClass *instance = [[[NSBundle mainBundle] loadNibNamed:@"MyClass" owner:nil options:nil] objectAtIndex:0]; 

Thanks in advance for any assitance.

在此先感谢您的帮助。

回答by Ismael

The second option is the correct one. The most defensive code you could do is like this:

第二种选择是正确的。你可以做的最具防御性的代码是这样的:

+ (id)loadNibNamed:(NSString *)nibName ofClass:(Class)objClass {
    if (nibName && objClass) {
        NSArray *objects = [[NSBundle mainBundle] loadNibNamed:nibName 
                                                         owner:nil 
                                                       options:nil];            
        for (id currentObject in objects ){
            if ([currentObject isKindOfClass:objClass])
                return currentObject;
        }
    }

    return nil;
}

And call like this:

并像这样调用:

MyClass *myClassInstance = [Utility loadNibNamed:@"the_nib_name" 
                                         ofClass:[MyClass class]]; 
// In my case, the code is in a Utility class, you should 
// put it wherever it fits best

I'm assuming your MyClass is a subclass of UIView? If that's the case, then you need to make sure that the UIViewof your .xib is actually of MyClass class. That is defined on the third Tab on the right-part in the interface builder, after you select the view

我假设您的 MyClass 是UIView? 如果是这种情况,那么您需要确保UIView.xib 的实际上是 MyClass 类。选择视图后,在界面构建器右侧的第三个选项卡上定义

回答by Rob

All you need to do is create the subview via loadNibNamed, set the frame, and add it to the subview. For example, I'm adding three subviews using my MyViewclass, which is a UIViewsubclass whose interface is defined in a NIB, MyView.xib:

您需要做的就是通过创建子视图loadNibNamed,设置框架,并将其添加到子视图中。例如,我使用我的MyView类添加三个子视图,这是一个UIView子类,其接口定义在 NIB 中MyView.xib

So, I define initWithFramefor my UIViewsubclass:

所以,我initWithFrame为我的UIView子类定义:

- (id)initWithFrame:(CGRect)frame
{
    NSLog(@"%s", __FUNCTION__);

    self = [super initWithFrame:frame];
    if (self)
    {
        NSArray *nibContents = 
          [[NSBundle mainBundle] loadNibNamed:@"MyView" 
                                        owner:self 
                                      options:nil];
        [self addSubview:nibContents[0]];
    }
    return self;
}

So, for example, in my UIViewController, I can load a couple of these subclassed UIViewobjects like so:

因此,例如,在 my 中UIViewController,我可以UIView像这样加载几个这些子类对象:

for (NSInteger i = 0; i < 3; i++)
{
    CGRect frame = CGRectMake(0.0, i * 100.0 + 75.0, 320.0, 100.0);
    MyView *myView = [[MyView alloc] initWithFrame:frame];
    [self.view addSubview:myView];

    // if you want, do something with it: 
    // Here I'm initializing a text field and label

    myView.textField.text = [NSString stringWithFormat:@"MyView textfield #%d",
                              i + 1];
    myView.label.text = [NSString stringWithFormat:@"MyView label #%d", 
                          i + 1];
}

I originally advised the use controllers, and I'll keep that answer below for historical reference.

我最初建议使用控制器,我将在下面保留该答案以供历史参考。



Original answer:

原答案:

I don't see any references to view controllers here. Usually you'd have a subclass of UIViewController, which you would then instantiate with

我在这里没有看到任何对视图控制器的引用。通常你会有一个 的子类 UIViewController,然后你会用它来实例化

MyClassViewController *controller = 
  [[MyClassViewController alloc] initWithNibName:@"MyClass" 
                                          bundle:nil];

// then you can do stuff like
//
// [self presentViewController:controller animated:YES completion:nil];

The NIB file, MyClass.xib, could specify that the base class for the UIView, if you want, where you have all of the view related code (e.g. assuming that MyClass was a subclass of UIView).

如果需要,NIB 文件MyClass.xib可以指定 的基类UIView,其中包含所有与视图相关的代码(例如,假设 MyClass 是 的子类UIView)。

回答by sooper

Here's one method that I use:

这是我使用的一种方法:

  1. Create a subclass for UIView, this will be called MyClass
  2. Create a view xib file. Open in interface builder, click File's Owner and in the Identity Inspector, change the class to that of your parent view controller, e.g. ParentViewController.
  3. Click the view already in the list of objects and change it's class in Identity Inspector to MyClass.
  4. Any outlets/actions that you declare in MyClasswill be connected by click-dragging from View (not File's Owner). If you want to connect them to variables from ParentViewControllerthen click-drag from File's Owner.
  5. Now in your ParentViewControlleryou need to declare an instance variable for MyClass.
  1. 为 创build一个子类UIView,这将被称为MyClass
  2. 创建视图 xib 文件。在界面生成器中打开,单击 File's Owner,然后在 Identity Inspector 中,将类更改为父视图控制器的类,例如 ParentViewController.
  3. 单击对象列表中已有的视图并将其在 Identity Inspector 中的类更改为MyClass
  4. 您在其中声明的任何出口/操作MyClass都将通过从视图(而不是文件的所有者)中单击拖动来连接。如果您想将它们连接到变量,ParentViewController然后单击并从 File's Owner 拖动。
  5. 现在,您ParentViewController需要为MyClass.

ParentViewController.hadd the following:

ParentViewController.h添加以下内容:

@class MyClass

@interface ParentViewController : UIViewController {
    MyClass *myClass;
}

@property (strong, nonatomic) MyClass *myClass;

Synthesize this in your implementation and add the following in your viewDidLoadmethod:

在您的实现中综合它并在您的viewDidLoad方法中添加以下内容 :

- (void)viewDidLoad {
    [super viewDidLoad];

    [[NSBundle mainBundle] loadNibNamed:@"MyClass" owner:self options:nil];
    self.myClass.frame = CGRectMake(X,Y,W,H); //put your values in.
    [self.view addSubview:self.myClass];
}