Бесконечный цикл при переопределении initWithCoder
У меня есть UIViewController
с некоторыми контроллерами и некоторыми представлениями. Два из этих представлений (Grid Cell) - это другие перья. У меня есть выходы из сетевых ячеек в File Owner, но они не загружаются автоматически.
Поэтому я пытаюсь переопределить GridCell.m
initWithCoder
.
Это запускает бесконечный цикл.
Я знаю, что можно просто переопределить initWithFrame
и добавить subview из кода, но это не то, что я хочу. Я хочу, чтобы иметь возможность перемещать представление в интерфейсе Builder и Xcode инициализировать представление с правильным фреймом.
Как мне добиться этого?
РЕДАКТИРОВАТЬ 1
Я пытаюсь заставить его работать с помощью Александра. Вот как я его создал:
MainView имеет UIView с пользовательским классом, установленным как GridCell. Он получил выход в MainView/File Owner.
Удален весь init-код из GridCell.m и настроен выход в мой пользовательский класс
Однако 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;
}