Подтвердить что ты не робот

IOS5, поведение UIScrollView и layoutSubviews

В моем приложении (код очень похож на демонстрацию Apple PhotoScroller от WWDC10) у меня есть UIScrollView. В представлении прокрутки я перезаписал layoutSubviews с кодом вроде:

- (void) layoutSubviews {
    [super layoutSubviews];
    // center the image as it becomes smaller than the size of the screen
    CGSize boundsSize = self.bounds.size;
    ...
 }

В iOS 4.x, если приложение запускается в ландшафтном режиме (пользователь держит его пейзаж), то layoutSubviews вызывается дважды (очень быстро). В первый раз, когда его вызываемый, boundsSize имеет размеры, которые указывают его на портрет, но сразу же его снова вызывают, а self.bounds.size возвращает размеры, указывающие на то, что устройство находится в ландшафтном режиме, и мои вычисления раскладки работают правильно.

В iOS 5.x layoutSubviews получает только один раз, когда bounds.size возвращает измерения, указывающие портрет, и он не получает второй вызов, поэтому весь мой код вычисления испорчен.

Если пользователь физически поворачивает устройство, то layoutSubviews вызывается и работает правильно - поэтому пользователь, запускающий приложение в ландшафтном режиме (неправильно рисует), поворачивается к портрету (рисует исправно), а затем поворачивается назад к пейзажу (теперь рисуется правильно).

Этот второй "автоматический" вызов layoutSubviews, который мне не хватает.

Кто-нибудь еще замечает это или имеет какой-либо совет?

Update:

Я нашел это в примечаниях к выпуску UIKit для iOS 5, но я не уверен, насколько он релевантен или даже влияет на это изменение.

Обратные вызовы вращения в iOS 5 не применяются для просмотра контроллеров, которые представлены в полноэкранном режиме. Это означает, что если ваш код представляет контроллер представления над другим контроллером представления, а затем пользователь впоследствии поворачивает устройство в другую ориентацию, при увольнении базовый контроллер (т.е. Представляющий контроллер) не получит никаких обратных вызовов вращения. Обратите внимание, однако, что представляющий контроллер получит вызов aviewWillLayoutSubviews, когда он будет повторно отображаться, и свойство interfaceOrientation можно запросить из этого метода и использовать для правильной компоновки контроллера.

Дополнительные обновления:

Используя [UIView recursiveDescription], чтобы сбросить иерархии представлений, я получил следующий вывод:

iOS 4 (который работает правильно):

Портрет:

2011-12-19 15:57:06.400 stroom[10889:11603] <0x5e7f9b0 stroomViewController.m:(402)> <UIView: 0x6a6f2f0; frame = (0 0; 768 1024); autoresize = W+H; layer = <CALayer: 0x6a659d0>>
   | <UIScrollView: 0x5e911e0; frame = (-20 0; 808 1024); clipsToBounds = YES; autoresize = LM+W+RM+TM+H+BM; layer = <CALayer: 0x5e91370>; contentOffset: {0, 0}>
   |    | <stroomFullScreenPhotoScrollView: 0x5ea1010; baseClass = UIScrollView; frame = (20 0; 768 1024); clipsToBounds = YES; layer = <CALayer: 0x5ea0940>; contentOffset: {0, 0}>
   |    |    | <UIImageView: 0x5ea2600; frame = (0 224; 768 576); transform = [0.75, 0, 0, 0.75, 0, 0]; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x5ea0890>>

Пейзаж:

2011-12-19 15:57:34.522 stroom[10889:11603] <0x5e7f9b0 stroomViewController.m:(402)> <UIView: 0x6d96c30; frame = (0 0; 768 1024); transform = [0, 1, -1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0x6d66440>>
   | <UIScrollView: 0x5e9eb70; frame = (-27 0; 1078 768); clipsToBounds = YES; autoresize = LM+W+RM+TM+H+BM; layer = <CALayer: 0x5eadde0>; contentOffset: {0, 0}>
   |    | <stroomFullScreenPhotoScrollView: 0x6dab850; baseClass = UIScrollView; frame = (20 0; 1038 768); clipsToBounds = YES; layer = <CALayer: 0x6dab2e0>; contentOffset: {0, 0}>
   |    |    | <UIImageView: 0x5e97f70; frame = (0 224; 1024 768); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x5e97fa0>>

iOS 5 (который работает некорректно):

Портрет:

 2011-12-19 15:55:59.530 stroom[10850:16103] <0x848b520 stroomViewController.m:(402)> <UIView: 0xa884710; frame = (0 0; 768 1024); autoresize = W+H; layer = <CALayer: 0xa8849a0>>
   | <UIScrollView: 0xa883820; frame = (-20 0; 808 1024); clipsToBounds = YES; autoresize = LM+W+RM+TM+H+BM; layer = <CALayer: 0xa883a80>; contentOffset: {0, 0}>
   |    | <stroomFullScreenPhotoScrollView: 0x8699630; baseClass = UIScrollView; frame = (20 0; 768 1024); clipsToBounds = YES; layer = <CALayer: 0x8699360>; contentOffset: {0, 0}>
   |    |    | <UIImageView: 0x869a7c0; frame = (0 0; 768 576); transform = [0.75, 0, 0, 0.75, 0, 0]; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x869a800>>

Пейзаж:

 2011-12-19 15:56:32.521 stroom[10850:16103] <0x848b520 stroomViewController.m:(402)> <UIView: 0x8498530; frame = (0 0; 768 1024); transform = [0, 1, -1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0x8498560>>
   | <UIScrollView: 0x849ead0; frame = (-27 0; 1077 768); clipsToBounds = YES; autoresize = LM+W+RM+TM+H+BM; layer = <CALayer: 0x848f390>; contentOffset: {808, 0}>
   |    | <stroomFullScreenPhotoScrollView: 0x81e4b80; baseClass = UIScrollView; frame = (828 0; 768 1024); clipsToBounds = YES; layer = <CALayer: 0x81e7dc0>; contentOffset: {0, 0}>
   |    |    | <UIImageView: 0x81e5090; frame = (0 0; 768 576); transform = [0.75, 0, 0, 0.75, 0, 0]; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x81e5010>>

Из них я вижу, что contentOffset UIScrollView в iOS 5 выглядит некорректно и что ландшафтный кадр имеет неправильные размеры в iOS 5, где они, похоже, имеют правильные размеры в iOS 4.

4b9b3361

Ответ 1

В конце концов, я наткнулся на решение этой проблемы для меня. Я приведу здесь ответ, поскольку он может быть полезен для других.

Решение для меня состояло в том, чтобы реализовать метод - (void)viewWillAppear:(BOOL)animated на моем контроллере представления. У меня его не было, и у меня была вся моя логика установки в методе - (void) viewDidLoad.

В частности, я реализовал следующее:

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];

     CGRect bounds = pagingScrollView.bounds; // <=== this was the key

     // ... use the bounds in my view setup logic
}

Мне кажется, что в iOS 5 произошли изменения (из предыдущих версий ОС). Когда представление загружается, когда устройство находится в ландшафтном режиме, свойство bounds на представлении не отражает преобразование ориентации до viewWillAppear. Раньше значение границ было правильным для меня в viewDidLoad, но перемещение вычислений и считывание свойства bounds оттуда и в viewWillAppear сделали трюк.

Все отлично работает в iOS4, когда я тестировал.

Возможно, я всегда должен был читать свойство bounds в viewWillAppear, и это может быть более корректное поведение. Однако что-то изменилось между iOS4 и iOS 5, что я не смог найти документацию.

`

Ответ 2

У меня была аналогичная проблема. Как-то я решил это. В iOS 5.x. Когда вы создаете новый XIB файл, по умолчанию для Orientation устанавливается значение Portrait. Таким образом, ваш layoutSubviews только получает вызов один раз с bounds.size возвращающими измерениями, указывающими портрет, и он не получает второй вызов.

Просто сделайте одно, установите ориентацию XIB в Landscape в дизайне XIB. Таким образом, ваш код будет проверять ориентацию в первый раз, он получит его как Пейзаж, и все ваши макеты будут установлены соответствующим образом.

Ответ 3

У меня такая же проблема. Я обошел это путем жесткого кодирования границ rect в зависимости от ориентации интерфейса контроллера представления и, возможно, помеченного scrollview для setNeedsLayout в viewDidLoad и viewWillAnimateToInterfaceOrientation.