Просмотр изменений кадра между viewWillAppear: и viewDidAppear:

85

Я обнаружил странное поведение в своем приложении, когда подключенный IBOutletимеет свой связанный фрейм представления между вызовами в моем контроллере представления к viewWillAppear:и viewDidAppear:. Вот соответствующий код в моем UIViewControllerподклассе:

-(void)viewWillAppear:(BOOL)animated {
    NSLog(@"%@", self.scrollView);
}

-(void)viewDidAppear:(BOOL)animated {
    NSLog(@"%@", self.scrollView);
}

и итоговый вывод журнала:

MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 0; 0 0); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}>
MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 44; 320 416); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}>

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

Джамхин
источник
2
Вы используете автопрокладку? вы добавляете это представление в построителе интерфейсов или программно?
Андреа
Включено автоматическое размещение, и это представление создается в IB из раскадровки.
Jumhyn
1
Я никогда не использовал раскадровку, но, скорее всего, это правильно. Использование Autolayout фрейм ваших представлений устанавливается, когда движок autolayout начинает свой расчет. Попробуйте задать то же самое сразу после super of - (void) viewDidLayoutSubviews mpethod вашего контроллера представления.
Андреа
Это успешно запускает мое событие в нужное время, но этот метод также вызывается всякий раз, когда я выполняю любую анимацию в представлении.
Jumhyn
1
viewDidLayoutSubviewsбыл правильный путь. Мне просто нужно было поместить весь мой контент в подпредставление, чтобы метод не вызывался повторно всякий раз, когда я менял фрейм основного представления.
Jumhyn

Ответы:

111

Autolayoutвнесла огромные изменения в то, как мы проектируем и разрабатываем графический интерфейс наших представлений. Одно из основных отличий заключается в том, что autolayoutразмеры наших представлений изменяются не сразу, а только при срабатывании, то есть в определенное время, но мы можем заставить его немедленно пересчитать наши ограничения или пометить их как «нуждающиеся в макете». Работает вроде -setNeedDisplay.
Для меня было очень сложно понять и принять тот факт, что нам больше не нужно использовать маски с автоизменением размеров, а фрейм стал бесполезным свойством при размещении наших представлений. Нам больше не нужно думать о позиции просмотра, но мы должны думать, как мы хотим видеть их в пространстве, связанном друг с другом.
Проблемы возникают тогда, когда мы хотим смешать старую маску с автоизменением размера и авторазложением. Мы должны очень скоро подумать о реализации автоматического размещения и постараться не смешивать старый подход в иерархии представлений, основанной на автоматическом размещении.
Допустимо иметь представление контейнера, в котором используются только маски с автоизменением размеров, такие как основное представление контроллера представления, но лучше, если мы не пытаемся смешивать.
Я никогда не использовал раскадровку, но, скорее всего, это правильно. Используя Autolayout, рамки ваших представлений устанавливаются, когда движок autolayout начинает свое вычисление. Попробуйте спросить то же самое сразу после super - (void)viewDidLayoutSubviewsметода вашего контроллера представления.
Этот метод вызывается, когда механизм автоматического раскладки завершает вычисление фреймов ваших представлений.

Андреа
источник
5
- (void) viewDidLayoutSubviews - это ответ для меня! Большое спасибо!
FrizzTheSnail 07
Этот ответ действительно неверен. Да, конечно, очевидно, в течение (5?) Лет вы должны использовать автопластинку. Но существует множество ситуаций ( с использованием автоопределения ), когда вам нужно, скажем, добавить что-то на экран «непосредственно перед тем, как это появится для пользователя». (Если вы сделаете это в viewDidAppear, вы получите мерцание. Если вы сделаете это в viewWillAppear - позиции будут неправильными.) Фактический ответ - действительно использовать viewDidLayoutSubviews.
Fattie
add something on a screen, "just before it appears to the user". The actual answer is indeed to use viewDidLayoutSubviews.... ты собираешься показывать это изображение много раз
Андреа
156

Из документации:

viewWillAppear:

Уведомляет контроллер представления о том, что его представление будет добавлено в иерархию представлений.

viewDidAppear:

Уведомляет контроллер представления о том, что его представление было добавлено в иерархию представлений.

В результате фреймы подвидов еще не установлены в viewWillAppear:

Подходящий метод для изменения вашего пользовательского интерфейса до того, как представление будет представлено на экране :

viewDidLayoutSubviews

Уведомляет контроллер представления о том, что его представление только что разместило свои подвиды.

Gsach
источник
2
Ух, это раздражает. Даже формулировка документации создает впечатление, что viewWillApepar: и viewDidAppear: должны происходить непосредственно друг за другом.
Jumhyn
2 месяца жду этого ответа ... спасибо, нашел !! БОЛЬШОЕ СПАСИБО!
Рафаэль Руис Муньос
6
Следует отметить, что viewDidLayoutSubviewsон будет вызываться несколько раз и не всегда с одним и тем же кадром (я думаю, что иногда он вызывается с помощью CGRectZero при первом вызове). Он вызывается для каждого добавленного подвида и других изменений в представлении.
bauerMusic
Я весь день вмешивался в это (viewDidLayoutSubviews вызывается несколько раз, в том числе при отклонении представления), и просто сказал с ним ef, и поместил логику в оператор if и сделал bool, которое становится истинным после код запускался один раз. Я думаю, что должен быть более чистый способ, но на него уже ушло слишком много времени.
соленоид
мы должны разобраться в этом! viewDidLayoutSubviews ужасно, seNeedDisplay не работает
Яро
9

вызов

self.scrollView.layoutIfNeeded ()

в вашем viewWillAppearметоде. После этого вы можете получить доступ к его фрейму, и он будет иметь то же значение, что и вы печатаете вviewDidAppear

андрей
источник
1
Нет, это не так. Пример: у вас есть панель навигации на экране (с вашего навигационного контроллера). Даже после layoutIfNeeded () высота панели навигации не включается, поэтому размер кадра изменится.
xaphod
Это IS хороший способ , чтобы заставить повторное вычисление размерности Scrollview кадра так , что оно соответствует всей иерархии вызовов вида макета , если кадр Scrollview связан с ограничениями в пределах его надтаблицы.
smakus
4

В моем случае перемещение всех связанных с кадром методов в

override func viewWillLayoutSubviews()

работал отлично (я пытался изменить ограничения из раскадровки).

Сиддхеш Махадешвар
источник