Поведение iOS5, UIScrollView и layoutSubviews - PullRequest
12 голосов
/ 18 октября 2011

В моем приложении (код очень похож на демонстрацию 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 возвращает измерения, которые указывают на то, что устройство находится в landsacpe, и мои расчеты макета работают правильно.

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

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

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

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

Обновление:

Я обнаружил это в примечаниях к выпуску 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.

Ответы [ 3 ]

2 голосов
/ 20 декабря 2011

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

Решением для меня было реализовать метод - (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 что-то изменилось, и мне не удалось найти документацию.

`

1 голос
/ 17 ноября 2011

У меня была похожая проблема.Я как-то решил это.В iOS 5.x, когда вы создаете новый файл XIB, по умолчанию для него устанавливается Ориентация «Портрет».Итак, ваш layoutSubviews вызывается только один раз с bounds.size, возвращающим размеры, указывающие на портрет, и он не получает второй вызов.

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

0 голосов
/ 31 октября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...