覆盖initWithCoder时的无限循环

时间:2012-07-12 10:51:40

标签: objective-c ios ios5 interface-builder storyboard

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

所以我尝试覆盖GridCell.m的{​​{1}}。 这开始了一个无限循环。

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

我如何实现这一目标?

编辑1

我正试着在Alexander的帮助下工作。这就是我现在设置的方式: MainView具有UIView,其Custom类设置为GridCell。它在MainView / File的所有者中有一个插座。

Pic 1

从GridCell.m中删除了所有初始化代码,并为我的自定义类

设置了一个插座

Pic 2

Pic 3

MainView不会显示GridCell。没有错误,只是一个孤独的,空的空间,红色开关应该是。我做错了什么?

我非常接近于以编程方式执行此操作。我很想知道如何使用笔尖。

5 个答案:

答案 0 :(得分:43)

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

答案 1 :(得分: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) 像在initWithCoder中一样自定义自定义视图中的initWithFrame方法:

- (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中,你想要包含自定义视图的视图的fileOwner(带有gridCellContainer视图的视图)在viewDidLoad中执行此操作,例如:

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

现在eveything应该按预期工作

答案 2 :(得分: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时,将实例作为File的所有者传递,将设置其所有IBOutlet。在这种情况下,您将使用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;
}

" How to implement a reusable UIView."有更详细的帖子。这也是一个更详细的细节,希望能够解决问题。

答案 3 :(得分:1)

loadNibNamed ::将调用initWithCoder:

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

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

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

       // do stuff here ... 

    }

    return self;                 
}

[super initWithcoder:coder]执行您想要避免的事情吗?

答案 4 :(得分: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;
}