TopLayoutGuide в контроллере детского просмотра
У меня есть UIPageViewController
с полупрозрачной панелью состояния и панель навигации. Его topLayoutGuide
составляет 64 пикселя, как и ожидалось.
Однако дочерние контроллеры представления UIPageViewController
сообщают a topLayoutGuide
из 0 пикселей, даже если они отображаются в строке состояния и на панели навигации.
Является ли это ожидаемым поведением? Если да, то какой лучший способ позиционировать представление контроллера детского представления под реальным topLayoutGuide
?
(не используя parentViewController.topLayoutGuide
, что я считаю взломом)
Ответы
Ответ 1
В то время как этот ответ может быть правильным, я все же обнаружил, что мне нужно перемещать дерево сдерживания, чтобы найти правильный родительский контроллер представления и получить то, что вы называете "real topLayoutGuide
". Таким образом, я могу вручную реализовать automaticallyAdjustsScrollViewInsets
.
Вот как я это делаю:
В моем контроллере представления таблицы (подклассом UIViewController
на самом деле), у меня есть это:
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
_tableView.frame = self.view.bounds;
const UIEdgeInsets insets = (self.automaticallyAdjustsScrollViewInsets) ? UIEdgeInsetsMake(self.ms_navigationBarTopLayoutGuide.length,
0.0,
self.ms_navigationBarBottomLayoutGuide.length,
0.0) : UIEdgeInsetsZero;
_tableView.contentInset = _tableView.scrollIndicatorInsets = insets;
}
Обратите внимание на методы категории в UIViewController
, вот как я их реализовал:
@implementation UIViewController (MSLayoutSupport)
- (id<UILayoutSupport>)ms_navigationBarTopLayoutGuide {
if (self.parentViewController &&
![self.parentViewController isKindOfClass:UINavigationController.class]) {
return self.parentViewController.ms_navigationBarTopLayoutGuide;
} else {
return self.topLayoutGuide;
}
}
- (id<UILayoutSupport>)ms_navigationBarBottomLayoutGuide {
if (self.parentViewController &&
![self.parentViewController isKindOfClass:UINavigationController.class]) {
return self.parentViewController.ms_navigationBarBottomLayoutGuide;
} else {
return self.bottomLayoutGuide;
}
}
@end
Надеюсь, что это поможет:)
Ответ 2
вы можете добавить ограничение в раскадровку и изменить его в viewWillLayoutSubviews
что-то вроде этого:
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
self.topGuideConstraint.constant = [self.parentViewController.topLayoutGuide length];
}
Ответ 3
Возможно, я ошибаюсь, но, на мой взгляд, поведение правильное. Значение topLayout может использоваться контроллером представления контейнера для компоновки его представлений вида.
В ссылке говорится:
Чтобы использовать верхний макет без использования ограничений, найдите положение направляющих относительно верхней границы содержащего представления.
В родительском отношении относительно содержащего представления значение будет равно 64.
В дочернем элементе, относительно содержащего представления (родительского), значение будет равно 0.
В контейнере View Controller вы можете использовать свойство следующим образом:
- (void) viewWillLayoutSubviews {
CGRect viewBounds = self.view.bounds;
CGFloat topBarOffset = self.topLayoutGuide.length;
for (UIView *view in [self.view subviews]){
view.frame = CGRectMake(viewBounds.origin.x, viewBounds.origin.y+topBarOffset, viewBounds.size.width, viewBounds.size.height-topBarOffset);
}
}
Контроллеру Child view не нужно знать, что есть панель навигации и состояния: ее родитель уже предусмотрел, что учитывает его subviews.
Если я создаю новый проект на основе страницы, встрою его в контроллер навигации и добавьте этот код в родительские контроллеры представлений, он работает нормально:
![enter image description here]()
Ответ 4
В документации говорится, что использовать topLayoutGuide в viewDidLayoutSubviews, если вы используете подкласс UIViewController, или layoutSubviews, если вы используете подкласс UIView.
Если вы используете его в этих методах, вы должны получить соответствующее ненулевое значение.
Ссылка на документацию:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instp/UIViewController/topLayoutGuide
Ответ 5
В случае, если у вас есть UIPageViewController
как OP, и у вас есть, например, контроллеры просмотра коллекции в качестве дочерних. Оказывается, исправление для вставки содержимого прост и работает на iOS 8:
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
UIEdgeInsets insets = self.collectionView.contentInset;
insets.top = self.parentViewController.topLayoutGuide.length;
self.collectionView.contentInset = insets;
self.collectionView.scrollIndicatorInsets = insets;
}
Ответ 6
Это было рассмотрено в iOS 8.
Как установить верхнюю позицию topLayoutGuide для контроллера детского просмотра
По существу, контроллер представления контейнера должен ограничивать контроллер дочернего представления (top|bottom|left|right)LayoutGuide
, как и любое другое представление. (В iOS 7 он уже был полностью ограничен требуемым приоритетом, поэтому это не сработало.)
Ответ 7
Я думаю, что гиды определенно должны быть установлены для вложенных дочерних контроллеров. Например, предположим, что у вас есть:
- Экран 100x50 с верхней строкой с 20 пикселями.
- Контроллер верхнего уровня, охватывающий все окно. Его topLayoutGuide - 20.
- Вложенный контроллер представлений внутри верхнего вида, охватывающий нижние 95 пикселей, например. 5 пикселей вниз от верхней части экрана. Это представление должно иметь topLayoutGuide из 15, так как его верхние 15 пикселей покрываются строкой состояния.
Это имело бы смысл: это означает, что контроллер вложенного представления может устанавливать ограничения для предотвращения нежелательного перекрытия, как и на верхнем уровне. Не нужно заботиться о том, чтобы он был вложен или где на экране отображается его родительский элемент, а родительский контроллер представления не должен знать, как ребенок хочет взаимодействовать со строкой состояния.
Это также похоже на то, что документация, или, по крайней мере, часть документации, говорит:
Верхняя направляющая макета указывает расстояние, в точках, между верхней частью представления контроллеров вида и нижней частью нижней панели, которая накладывает вид
(https://developer.apple.com/library/ios/documentation/UIKit/Reference/UILayoutSupport_Protocol/Reference/Reference.html)
Это ничего не говорит о работе только для контроллеров верхнего уровня.
Но я не знаю, действительно ли это происходит. Я определенно видел контроллеры детского просмотра с ненулевыми topLayoutGuides, но я все еще разбираюсь в причудах. (В моем случае верхняя направляющая должна быть нулевой, так как представление не находится в верхней части экрана, и это то, с чем я стучу головой в данный момент...)
Ответ 8
Это подход для известной длины направляющей. Создайте ограничения не для направляющих, а для просмотра верха с фиксированными константами, предполагая, что расстояние между направляющими будет.
Ответ 9
Быстрая реализация ответа @NachoSoto:
extension UIViewController {
func navigationBarTopLayoutGuide() -> UILayoutSupport {
if let parentViewController = self.parentViewController {
if !parentViewController.isKindOfClass(UINavigationController) {
return parentViewController.navigationBarTopLayoutGuide()
}
}
return self.topLayoutGuide
}
func navigationBarBottomLayoutGuide() -> UILayoutSupport {
if let parentViewController = self.parentViewController {
if !parentViewController.isKindOfClass(UINavigationController) {
return parentViewController.navigationBarBottomLayoutGuide()
}
}
return self.bottomLayoutGuide
}
}
Ответ 10
Не уверен, что у кого-то все еще есть проблемы с этим, поскольку я все еще сделал несколько минут назад.
Моя проблема как это (источник gif от https://knuspermagier.de/2014-fixing-uipageviewcontrollers-top-layout-guide-problems.html).
Короче говоря, у моего pageViewController есть 3 дочерних контроллера. Первый диспетчер представлений отлично, но когда я перехожу к следующему, весь вид неправильно смещен на верхний (примерно 20 пикселов), но вернется в нормальное состояние после того, как мой палец выключен.
Я не спал всю ночь, ища решения для этого, но мне все равно не повезло.
Внезапно я придумал эту сумасшедшую идею:
[pageViewController setViewControllers:@[listViewControllers[1]] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:^(BOOL finished) {
}];
[pageViewController setViewControllers:@[listViewControllers[0]] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:^(BOOL finished) {
}];
В моем спискеViewControllers есть 3 дочерних контроллера. У объекта с индексом 0 есть проблема, поэтому я сначала установил его как root из pageviewcontroller, и сразу после этого вернул его в первый контроллер представления (как я и ожидал).
Вуала, это сработало!
Надеюсь, это поможет!