14

我有UIViewController一些控制器和一些视图。其中两个视图(网格单元)是其他 nib。我有从网格单元到文件所有者的出口,但它们不会自动加载。

所以我尝试覆盖GridCell.m. initWithCoder这将启动一个无限循环。

我知道可以initWithFrame从代码中覆盖和添加子视图,但这不是我想要的。我希望能够在 Interface Builder 中移动视图并让 Xcode 使用正确的框架初始化视图。

我该如何实现这一目标?

编辑 1

我正试图在亚历山大的帮助下让它工作。这就是我现在设置它的方式:MainView 具有 UIView,其自定义类设置为 GridCell。它在 MainView/File's Owner 中有一个出口。

图一

从 GridCell.m 中删除所有初始化代码并为我的自定义类设置一个出口

图二

图 3

MainView 仍然不显示 GridCell。没有错误,只是一个孤独的、空白的地方,红色开关应该在的地方。我究竟做错了什么?

我非常接近以编程方式执行此操作。不过,我很想用笔尖学习如何做到这一点。

4

5 回答 5

45

加载 nib 会导致再次调用 initWithCoder,因此如果子类当前没有任何子视图,您只想这样做。

-(id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        if (self.subviews.count == 0) {
            UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil];
            UIView *subview = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
            subview.frame = self.bounds;
            [self addSubview:subview];
        }
    }
    return self;
}
于 2012-07-31T15:33:12.090 回答
11

加载一个笔尖会导致相应的所有者在一个

  -(id) initWithCoder:(NSCoder *) coder;  

称呼

因此,您在此方法中的代码:

self = [[[NSBundle mainBundle] loadNibNamed: @"GridCell"
owner: self
options: nil] objectAtIndex:0];

将再次调用 initWithCoder 方法。那是因为您尝试再次加载笔尖。如果你定义一个自定义 UIView 并创建一个 nib 文件来布置它的子视图,你不能只是将一个 UIView 添加到另一个 nib 文件,将 IB 中的类名更改为你的自定义类并期望 nib 加载系统能够解决它。您可以执行以下操作:

您的自定义视图的 nib 文件需要将“文件的所有者”类设置为您的自定义视图类,并且您需要在自定义类中有一个名为“toplevelSubView”的插座连接到您的自定义视图 nib 文件中的视图,该视图充当所有子视图的容器。向您的视图类添加额外的出口并将子视图连接到“文件所有者”(您的自定义 UIView)。(见https://stackoverflow.com/a/7589220/925622

编辑 好的,要回答您编辑的问题,我将执行以下操作:

转到要包含自定义视图的 nib 文件,并使用它的 nib 文件对其进行布局。不要进入自定义视图 (GridCell) 本身,而是创建一个包含网格单元格的视图(例如,gridCellContainer,但它应该是 UIView) 在自定义视图中自定义 initWithFrame 方法,就像在 initWithCoder 中所做的那样:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"GridCell" owner:self options:nil];    
        self = [nib objectAtIndex:0];
        self.frame = frame;
    }
    return self;
}

然后,在 viewController 中,它是要包含自定义视图(具有 gridCellContainer 视图的视图)的视图的 fileOwner,在 viewDidLoad 中执行此操作,例如

//...
GridCell *gridCell = [[GridCell alloc] initWithFrame:self.viewGridCellContainer.bounds];
[self.viewGridCellContainer addSubview:gridCell];

现在一切都应该按您的预期工作

于 2012-07-12T11:22:54.870 回答
2

文件的所有者不会接到电话

  -(id) initWithCoder:(NSCoder *) coder;  

加载 xib 时。

但是,该 xib 中定义的每个视图都会调用

-(id) initWithCoder:(NSCoder *) coder;  

加载 xib 时。

如果您在 xib 中定义了 UIView 的子类(即 GridCell),并且还尝试在子类的 initWithCoder 中加载相同的 xib,您将最终陷入无限循环。但是,我看不到用例会是什么。

通常,您在一个 xib 中设计 UIView 的子类(即 GridCell),然后在视图控制器的 xib 中使用该子类。

此外,看不到您的自定义视图将在其 initWithCoder 中包含子视图的用例,即

-(id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        if (self.subviews.count == 0) {
            UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil];
            UIView *subview = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
            subview.frame = self.bounds;
            [self addSubview:subview];
        }
    }
    return self;
}

除非您希望能够在其他一些 xib 中按需覆盖其视图层次结构。哪个 IMO 假定一个外部依赖项(即在另一个 xib 中定义的层次结构)并且有点违背了首先拥有可重用 UIView 的目的。

请记住,在加载 xib 时,将实例作为文件的所有者传递,将设置其所有 IBOutlet(s)。在这种情况下,您将用 GridCell.xib 中的任何根视图替换 self(即 GridCell),从而在此过程中丢失所有 IBOutlet 连接。

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"GridCell" owner:self options:nil];    
        self = [nib objectAtIndex:0];
        self.frame = frame;
    }
    return self;
}

有一篇关于“如何实现可重用的 UIView ”的更详细的帖子。它也更详细地介绍了一些内容,并希望能把事情弄清楚。

于 2014-09-25T00:03:53.740 回答
1

loadNibNamed:: 将调用 initWithCoder:

你为什么不遵循这个模式?

-(id)initWithCoder:(NSCoder *)coder
{

    if (self = [super initWithcoder:coder]) {

       // do stuff here ... 

    }

    return self;                 
}

[super initWithcoder:coder]你想避免的事情吗?

于 2012-07-12T11:07:40.010 回答
0

当我尝试覆盖initWithsomething方法时,我遇到了同样的问题,我们需要

-(id)initWithsomething:(Something *)something
{
    if (self = [super initWithsomething:something]) {
       // do stuff here ... 
    }

    return self; 
}

反而

-(id)initWithsomething:(Something *)something
{
    if (self = [super init]) {
       // do stuff here ... 
    }

    return self;
}
于 2012-11-13T11:32:19.070 回答