Бесконечный цикл при переопределении initWithCoder

У меня есть UIViewController с некоторыми контроллерами и некоторыми представлениями. Два из этих представлений (Grid Cell) - это другие перья. У меня есть выходы из сетевых ячеек в File Owner, но они не загружаются автоматически.

Поэтому я пытаюсь переопределить GridCell.m initWithCoder. Это запускает бесконечный цикл.

Я знаю, что можно просто переопределить initWithFrame и добавить subview из кода, но это не то, что я хочу. Я хочу, чтобы иметь возможность перемещать представление в интерфейсе Builder и Xcode инициализировать представление с правильным фреймом.

Как мне добиться этого?

РЕДАКТИРОВАТЬ 1

Я пытаюсь заставить его работать с помощью Александра. Вот как я его создал: MainView имеет UIView с пользовательским классом, установленным как GridCell. Он получил выход в MainView/File Owner.

Pic 1

Удален весь init-код из GridCell.m и настроен выход в мой пользовательский класс

Pic 2

Pic 3

Однако MainView пока не отображает GridCell. Там нет ошибки, просто одинокое пустое место, где должен быть красный переключатель. Что я делаю неправильно?

Я очень близок к тому, чтобы делать это программно. Я бы с удовольствием узнал, как это с помощью носков.

Ответ 1

Загрузка 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;
}

Ответ 2

Загрузка нити вызовет соответствующего владельца в

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

вызов

Поэтому ваше coude в этом методе:

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

снова вызовет вызов метода initWithCoder. Это потому, что вы пытаетесь снова загрузить нить. Если вы определяете пользовательский UIView и создаете файл nib для выкладки своих подзонов, вы не можете просто добавить UIView в другой файл nib, измените имя класса в IB на свой собственный класс и ожидайте, что система загрузки nib будет выяснять это. Вы можете сделать следующее:

В вашем пользовательском представлении nib файла должен быть установлен класс "Владелец файла" для вашего пользовательского класса просмотра, и вам нужно иметь выход в свой пользовательский класс под названием "toplevelSubView", подключенный к представлению в вашем пользовательском файле nib файла, который выступая в качестве контейнера для всех подзонов. Добавьте дополнительные выходы в класс просмотра и подключите подпрограммы к "Владелец файла" (ваш пользовательский UIView). (См. fooobar.com/questions/9539/...)

ИЗМЕНИТЬ Хорошо, чтобы ответить на ваш отредактированный вопрос, я бы сделал следующее:

Перейдите в файл 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), делает это в viewDidLoad, например.

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

Теперь все должно работать так, как вы ожидали

Ответ 3

Владелец файла не получит вызов

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

при загрузке xib.

Однако каждое представление, определенное в этом xib, получит вызов

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

при загрузке xib.

Если у вас есть подкласс UIView (т.е. GridCell), определенный в xib, а также попытайтесь загрузить тот же самый xib в вашем подклассе initWithCoder, вы получите бесконечный цикл. Однако я не вижу, что будет в случае использования.

Обычно вы создаете свой подкласс UIView (т.е. GridCell) в одном xib, а затем используете этот подкласс в контроллерах вида xib, например.

Кроме того, не удается увидеть прецедент, в котором у вашего пользовательского представления будет subview в нем 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. Какая ИМО принимает внешнюю зависимость (т.е. Иерархию, определенную в другом xib), и, как следствие, побеждает цель использования повторно используемого UIView.

Не забывайте, что при загрузке xib, передав экземпляр в качестве владельца файла, будет установлен все его IBOutlet (s). В этом случае вы бы заменили self (т.е. GridCell) каким бы корневым представлением этого GridCell.xib не потерял все соединения 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.", который также идет более подробно и, надеюсь, очищает вещи вверх.

Ответ 4

loadNibNamed:: вызовет initWithCoder:

Почему бы вам не следовать этому шаблону?

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

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

       // do stuff here ... 

    }

    return self;                 
}

Делает ли [super initWithcoder:coder] то, чего вы хотите избежать?

Ответ 5

У меня была такая же проблема, когда я пытаюсь переопределить метод 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;
}