Корректная реализация loadView
Документы Apple не говорят, какая правильная реализация для loadView.
Я обнаружил, что если вы реализуете loadView следующим образом:
- (void)loadView
{
self.view = [[UIView alloc] init];
}
... тогда вы получите другое поведение, чем если бы вы его вообще не реализовали. В частности, в одном проекте из 20 строк я обнаружил, что viewWillAppear вызывается с фреймом нулевого размера для self.view - если вы не используете версию LoadView по умолчанию Apple по умолчанию.
Глядя на Google, существует множество "обучающих программ", которые обеспечивают явно неправильные реализации loadView - например, force-setting size (320,480), потому что автор учебника "обнаружил, что он работает, если я это делаю".
Я хотел бы знать, какова должна быть правильная реализация.
NB: в моем примере выше я добавляю его в иерархию представлений внутри AppDelegate следующим образом:
[self.window addSubview:(UIViewController*).view];
Я считаю, что в присутствии UINavigationController или UITabBarController Apple делает некоторую дополнительную магию, которая - как побочный эффект - заставляет однострочную реализацию loadView работать нормально. Но я хочу написать это правильно, чтобы он всегда работает!
NB: я попытался установить маску авторезиста в корневом представлении, но это не меняет того, что происходит:
- (void)loadView
{
self.view = [[UIView alloc] init];
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
Ответы
Ответ 1
Реализация по умолчанию -loadView
создает представление или загружает NIB. Насколько мне известно, нет способа узнать конечный размер представления в момент создания в -loadView
. Таким образом, размер представления по умолчанию установлен на [[UIScreen mainScreen] bounds]
. Это связано с тем, что работать с представлением нулевого кадра в -viewDidLoad
и другими методами может быть сложно.
Ваша однострочная реализация может выглядеть так:
- (void)loadView {
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
}
Вам не нужно устанавливать маску авторезистации, потому что вы не знаете, в каком контексте будет отображаться представление. Вызывающий отвечает за то, чтобы установить правильный фрейм, авторезистирующую маску и аналогичные внешние атрибуты (я их называю так).
Представьте это в методе UINavigationController
:
// we are pushing new VC, view is accessed for the first time
pushedVC.view.frame = CGRectMake(...);
Он устанавливает правильный фрейм, но ваш -loadView
вызывается только до, который -setFrame:
. Таким образом, во время -viewDidLoad
у вас есть временный ненулевой фрейм, чтобы иметь возможность настраивать subviews и внутреннюю автореализацию. После этого правильный фрейм установлен для вас, а в -viewWillAppear:
у вас есть окончательный кадр.
Ответ 2
Во-первых, нет реализации по умолчанию loadView
... этот метод специально предназначен для вас. Я согласен с тем, что документы Apple могут быть немного неясными. Но loadView
вызывается по умолчанию всякий раз, когда открывается доступ к просмотру навигационного контроллера, и нет представления (например: UIView *view = viewController.view
). Его также можно вызвать вручную. Но ни в коем случае loadView
не имеют правильных размеров... то есть, по сути, невозможно. loadView
вызывается для того, чтобы контроллер родительского представления получил представление в первую очередь, чтобы он мог соответствующим образом изменить его. Затем, получив представление, он вызывает viewDidLoad
. Это единственный путь к коду, который они могут использовать, поскольку представления могут загружаться из метода loadView
или nib, и они должны предоставить место для дополнительной настройки, когда представления загружаются из наконечника. Наконец, родительский контроллер изменит размер представления и вызовет viewWillAppear
только тогда, когда представление действительно появится. Например, если вы нажмете контроллер на navController, который выключен, он не вызовет viewWillAppear
, пока сам navController не будет помещен на экран. Это делается потому, что нет смысла запускать этот код, пока контроллер не будет виден. Именно поэтому вы можете получить только правильное измерение в методе viewWillAppear
.
Теперь вы заметили, что если вы добавите контроллер в стандартный контроллер, ничего из этого не произойдет. Это связано с тем, что диспетчеры представления на самом деле не предназначены для размещения других контроллеров представлений для каждого сообщения. Теперь в iOS 5 они явно поддерживают использование Container View Controllers... который по существу является контроллером представления, который предназначен для размещения других контроллеров представлений. Они добавили несколько "удобных" методов в iOS 5, чтобы помочь в этом, но это не является строго необходимым. Суть всего этого заключается в следующем: если вы хотите добавить один контроллер просмотра в другой, вам придется вручную настроить все соответствующие вызовы на контроллер дочернего представления (все методы загрузки, события поворота, предупреждение о памяти и т.д.). Другими словами, вы должны создать свой собственный контроллер контейнера. Однако, когда вы это делаете, имейте в виду, что я сказал раньше о пути кода. Важно, чтобы вы называли методы контроллера дочерних элементов в том же порядке, что и Apple, или вещи не будут работать правильно.
Вот некоторые ссылки на информацию:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html -Свернуть вниз: Реализация контроллера просмотра контейнера
Также здесь для жизненного цикла контроллера просмотра, который поможет вам определить, какие вызовы необходимо выполнить в каком порядке: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html#//apple_ref/doc/uid/TP40007457-CH10-SW1
Я действительно рекомендую прочитать весь Руководство по программированию на View Controller.... вы можете получить оттуда много информации: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html#//apple_ref/doc/uid/TP40007457-CH1-SW1