Не может обновить view.layer.frame в viewDidLoad?
Я пытаюсь выполнить следующий код в методе viewDidLoad моего проекта с одним контроллером представлений:
self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
Но он не дает желаемой вставки. Однако другие изменения пользовательского интерфейса, которые я делаю в одном и том же методе, работают, например,
self.view.layer.backgroundColor = [UIColor orangeColor].CGColor;
Над строкой кода работает, а фон меняется на оранжевый, но кадр не работает.
Вставка работает только в том случае, если я помещаю строку кода в viewDidAppear. Я хотел бы понять основную причину такого поведения, если кто-нибудь сможет объяснить. Заранее благодарю вас.
Ответы
Ответ 1
Метод viewDidLoad
слишком рано устанавливать рамку представления, потому что что-то еще меняет кадр позже.
Например, если это ваш корневой контроллер, тогда объект UIWindow
будет устанавливать рамку представления, когда он добавит представление в себя как подвью. Метод viewDidLoad
запускается, как только loadView
возвращается, прежде чем какой-либо внешний объект (например, UIWindow
) может получить доступ к представлению.
Вы можете протестировать это, используя пользовательское представление (если вы еще этого не сделали) и переопределить setFrame:
в пользовательском классе вида:
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
}
Поместите точку останова в этот метод, и вы увидите, что рамка представления устанавливается через некоторое время после возвращения viewDidLoad
.
Ответ 2
Я думаю, что проблема, с которой вы столкнулись с этой строкой:
self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
можно объяснить следующим образом:
- в viewDidLoad, свойства установлены, но фрейм просмотров еще не определен.
- к моменту представления viewWillAppear, они будут установлены. Это объясняет, почему эта строка кода работает там.
- Но с iOS 5 существует еще один метод, который вызывается после viewDidLoad и перед viewWillAppear, в котором установлены кадры представления: viewDidLayoutSubviews.
Вы можете найти полное объяснение этого в текущем сезоне в Stanford CS193P о программировании iOS (кстати, очень круто).
Итак, если вы хотите, чтобы он работал только один раз, используйте:
- (void)viewDidLayoutSubviews
{
self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
}
PS: Я отправил этот ответ на форум Рей.
С Уважением,
Фред
Ответ 3
Ответ Роб Майофф правильный и превосходный, но несколько отличается: viewDidLoad
означает, что просмотр загружен, т.е. что контроллер представления получил свое представление. Это не означает, что представление было помещено в интерфейс. Это, действительно, одна из вещей, которые означает viewDidAppear:
, и почему она работала, когда вы запускали свой код там.
Трюк в такой ситуации, когда вы хотите инициализировать что-то о представлении, заключается в том, чтобы сделать это достаточно поздно, но делать это только один раз. viewDidAppear:
можно было бы снова вызвать позже, но вы не хотите снова инициализировать представление (если оно не было выгружено). В iOS 5, isMovingToParentViewController
позволяет различать конкретные обстоятельства, которые вы ищете. До этого может потребоваться установить флаг BOOL, чтобы вы выполняли окончательные инициализации только один раз.
Связанная ловушка - это то, что происходит, когда приложение запускается в альбомную ориентацию. И здесь слишком скоро viewDidLoad
, потому что интерфейс еще не повернулся в ландшафт.
Однако этот вопрос не должен возникать вообще. Не должно быть ни одного вашего бизнеса, чтобы вставить вид контроллера контроллера. Либо представление является контроллером корневого представления, и в этом случае его размер правильно обрабатывается автоматически, либо он является дочерним элементом родительского контроллера представления, и в этом случае задание контроллера родительского представления должно определять размер (как UINavigationController, например, уже делает), или представление должно быть представлено модально, и в этом случае его размер будет автоматически установлен в соответствии с замещаемым им видом. Поэтому я бы предположил, что вы очень сомневаетесь в том, что делаете что-то неправильно.
Ответ 4
Создайте свой проект с более старой версией Xcode (например, я использую для этого Xcode 4.3.3). Затем вы можете использовать setFrame: метод с viewDidLoad в любой версии Xcode.
Ответ 5
Я также испытал эту проблему в учебнике Ray Wenderlich "Введение в CALayers".
http://www.raywenderlich.com/2502/introduction-to-calayers-tutorial
Кажется, сокращение всего представления контроллеров View - это не лучшая вещь. Вместо этого создайте суб-представление и вызовите CGRectInset, например, в вашем представлении контроллеры viewDidLoad
UIView *viewToManipulate = [[UIView alloc] initWithFrame:CGRectMake(0.0,0.0, self.view.bounds.size.width, self.view.bounds.size.height)];
[self.view addSubview:viewToManipulate];
viewToManipulate.backgroundColor = [UIColor redColor];
viewToManipulate.layer.cornerRadius = 20.0;
viewToManipulate.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);