iPhone SDK: в чем разница между loadView и viewDidLoad?

136

При работе с представлениями и контроллерами представления в приложении для iPhone, кто-нибудь может объяснить разницу между loadView и viewDidLoad?

Мой личный контекст заключается в том, что я строю все свои представления из кода, я не использую и не буду использовать Interface Builder, если это что-то изменит.

Я обнаружил, что часто, когда я добавляю код инициализации в loadView, я получаю бесконечную трассировку стека, поэтому я обычно делаю все свои дочерние представления в viewDidLoad ... но мне действительно неясно, когда выполняется каждый из них, и что является более подходящим местом для установки кода инициализации. Что было бы идеально, так это простая схема инициализации вызовов.

Спасибо!

ryan.scott
источник

Ответы:

200

Я могу догадаться, в чем может быть проблема здесь, потому что я сделал это:

Я обнаружил, что часто, когда я добавляю код инициализации в loadView, я получаю бесконечную трассировку стека

Не читайте self.view в -loadView. Только установите это, не получите это.

Метод доступа к свойству self.view вызывает -loadView, если представление в данный момент не загружено. Это твоя бесконечная рекурсия.

Обычный способ построить представление программно в -loadView, как продемонстрировано в примерах Apple, предшествовавших созданию интерфейса, похож на это:

UIView *view = [[UIView alloc] init...];
...
[view addSubview:whatever];
[view addSubview:whatever2];
...
self.view = view;
[view release];

И я не виню вас за то, что вы не используете IB. Я придерживался этого метода для всех Instapaper и чувствую себя гораздо более комфортно с ним, чем справляюсь со сложностями IB, особенностями интерфейса и неожиданным закулисным поведением.

Marco
источник
аааа, спасибо за объяснение, наконец-то! Я уклонялся от идиомы выделения временной переменной, затем установки на self.view, затем освобождения ... это казалось каким-то неловким, ненужным. Теперь я могу понять, почему это решение привело бы меня к тому пути, на котором я сейчас оказался.
ryan.scott
У меня есть такой код и нет рекурсии. Зачем? -(void) loadView { // Frame for Hypnosis view CGRect frame = [[UIScreen mainScreen] bounds]; // Create a Hipnosis view v = [[HypnosisView alloc] initWithFrame:frame]; self.view = v;
user2054339
44

loadViewэто метод UIViewController, который фактически загружает представление и присваивает его viewсвойству. Это также местоположение, которое UIViewControllerбудет переопределять подкласс , если вы хотите программно настроить viewсвойство.

viewDidLoadэто метод, который вызывается после загрузки представления. Это вызывается после вызова loadView. Это место, где вы можете переопределить и вставить код, который выполняет начальную настройку представления после его загрузки.

NilObject
источник
14
viewDidLoad()

должен использоваться, когда вы загружаете ваше представление из NIB и хотите выполнить любую настройку после запуска

LoadView()

должен использоваться, когда вы хотите создать свое представление программно (без использования Interface Builder)

ashokdy
источник
Это может иметь некоторую проблему, у меня есть тест, когда мой контроллер представления не был связан с файлом NIB, viewDidLoad все еще вызывал
ruandao
11

Просто добавьте несколько примеров кода, чтобы продемонстрировать, что сказал NilObject:

- (void)loadView
{
    // create and configure the table view
    myTableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStyleGrouped];   
    myTableView.delegate = self;
    myTableView.dataSource = self;
    myTableView.scrollEnabled = NO;
    self.view = myTableView;

    self.view.autoresizesSubviews = YES;
}

- (void)viewDidLoad 
{
  self.title = @"Create group";

  // Right menu bar button is to Save
  UIBarButtonItem *saveButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Save" style:UIBarButtonItemStyleDone target:self action:@selector(save)];
  self.navigationItem.rightBarButtonItem = saveButtonItem;
  [saveButtonItem release];
}
alamodey
источник
4
Итак, между вами двумя, правильно ли говорить, что loadView - это то место, где я должен делать alloc / init для моего контроллера self.view, а дочерние представления должны обрабатываться в viewDidLoad (или позже)?
ryan.scott
2

Чтобы предотвратить бесконечный цикл при чтении self.view, вызывайте супер реализацию класса при загрузке представления. Супер реализация выделит вам новый UIView.

- (void) loadView {
[super loadview];

// init code here...

[self.view addSubView:mySubview1]; //etc..

}
futureelite7
источник
6
Я могу поклясться, что в документации Apple сказано, что не стоит звонить [super loadView];. Это противоречило в примерах, но я думаю, что документы сказали это правильно (со временем я обнаружил многочисленные ошибки в примерах). [super loadView]требуется для UITableViewController и т. д. Тем не мение! Любая установка после загрузки (например, добавление дополнительных подпредставлений) должна быть сделана в viewDidLoad.
Иван Вучица,
Я назвал [super loadView] без каких-либо побочных эффектов. Это может быть правдой, если вы намереваетесь настроить self.view на то, что вы сделали сами.
futureelite7
Если вы вызовете [super loadView] внутри loadView, он попытается загрузить представление из пера, если оно доступно с именем по умолчанию. Так что нужно быть осторожным.
Ian1971
И если вы вызываете [super loadView], вы инициализируете self.view в методе super loadView
Алекс Назарский
1

Самый простой способ использовать loadView - это создать некоторый тип контроллера базового представления, например MyBaseViewController, который является подклассом UIViewController. В его методе loadView создайте представление следующим образом:

-(void) loadView {
    if ([self viewFromNib]) {
        self.view = [self viewFromNib];
    } else {
        self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    }
    self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight;
    self.view.backgroundColor = [UIColor whiteColor];
}

И когда вам нужно сделать какой-то контроллер представления, вы просто используете подкласс MyBaseViewController, а в его контроллере loadView вы просто вызываете [super loadView], как это

//sucblass loadView
-(void) loadView {
    [super loadView];

    //rest of code like this..
    UILabel *myLabel = [[UILabel alloc] initWithFrame:myFrame];
    [self.view addSubview:myLabel];
    [myLabel release];
}
Иосип Б.
источник
1

loadView()вызывается, когда ваш контроллер попросили создать его self.view. Вы можете сделать это самостоятельно, как

self.view = [UIView alloc] init...];

Или родительский класс UIController вашего контроллера уже имеет имя метода, -loadView()которое инициализирует ваш self.view в пустое представление. Тогда вы можете позвонить

[super loadView];

Я действительно рекомендую второй подход, поскольку он поощряет наследование. Только если ваш контроллер представления не наследуется напрямую от UIViewController.

Дулгуун Отгон
источник
0

В определении, данном Apple для viewDidLoad, упоминается, что он вызывается после загрузки представления контроллера в память. Проще говоря, это первый метод, который будет загружаться.

Вы можете подумать, при каких условиях этот метод будет полностью использован? Ответ в основном то, что вы хотели, чтобы приложение загружалось первым. Например, вам может потребоваться другой цвет фона, вместо белого, вы можете выбрать синий.

Гульсан Борбхуйя
источник