Не может обновить 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);