topLayoutGuide в ребенка-представление-контроллер
у меня есть UIPageViewController
с прозрачный строка состояния и панель навигации. Его topLayoutGuide
на 64 пикселей, как и ожидалось.
однако дочерние контроллеры представления UIPageViewController
отчет topLayoutGuide
0 пикселей, даже если они отображаются в строке состояния и панели навигации.
это ожидаемое поведение? Если да, то каков наилучший способ расположить представление контроллера дочернего представления под реальным topLayoutGuide
?
(за исключением использования parentViewController.topLayoutGuide
, который я бы рассмотрел hack)
11 ответов:
пока ответ может быть правильно, я все еще обнаружил, что мне нужно путешествовать по дереву сдерживания, чтобы найти правильный контроллер родительского вида и получить то, что вы описываете как "реальный
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
надеюсь, что это помогает :)
вы можете добавить ограничение в раскадровке и изменить его в viewWillLayoutSubviews
что-то вроде этого:
- (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; self.topGuideConstraint.constant = [self.parentViewController.topLayoutGuide length]; }
Я могу ошибаться, но на мой взгляд поведение является правильным. Значение topLayout может использоваться контроллером вида контейнера для компоновки его подвидов.
ссылка говорит:
чтобы использовать верхнюю направляющую компоновки без использования ограничений, получите положение направляющей относительно верхней границы содержащего вида.
в Родительском представлении, относительно содержащего представления, значение будет 64.
в дочернее, относительно содержащего представления (родителя), значение будет равно 0.
в контроллере представления контейнера вы можете использовать свойство следующим образом:
- (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); } }
контроллер дочернего вида не должен знать, что есть навигация и строка состояния : его родитель уже выложил свои подвиды с учетом этого.
если я создаю новый проект на основе страницы, вставьте его в навигационный контроллер и добавьте этот код в родительское представление контроллеры вроде бы работают нормально:
в документации говорится об использовании 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
в случае если у вас есть
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; }
Это было устранено в iOS 8.
Как установить положение topLayoutGuide для контроллера детского вида
по существу, контроллер представления контейнера должен ограничивать
(top|bottom|left|right)LayoutGuide
как и любой другой вид. (В iOS 7 он уже был полностью ограничен с требуемым приоритетом, поэтому это не сработало.)
Я думаю, что направляющие определенно предназначены для установки для вложенных дочерних контроллеров. Например, предположим, что у вас есть:
- экран 100x50, с 20-пиксельной строкой состояния в верхней части.
- контроллер вида верхнего уровня, охватывающий все окно. Его topLayoutGuide составляет 20.
- вложенный контроллер вида внутри верхнего вида, охватывающий нижние 95 пикселей, например. 5 пикселей вниз от верхней части экрана. Этот вид должен иметь topLayoutGuide 15, так как его верхние 15 пикселей покрываются строкой состояния.
Это имеет смысл: это означает, что контроллер вложенного представления может устанавливать ограничения для предотвращения нежелательного перекрытия, как и на верхнем уровне. Ему не нужно заботиться о том, что он вложен или где на экране его родитель отображает его, и контроллеру родительского представления не нужно знать, как ребенок хочет взаимодействовать со строкой состояния.
Это также похоже на то, что документация-или некоторые из документации, по крайней мере ... говорит:
верхнее руководство по компоновке указывает расстояние в точках между верхней частью представления контроллера вида и нижней частью самой нижней панели, которая накладывается на вид
Это ничего не говорит о работе только для просмотра верхнего уровня контроллеры.
но, я не знаю, если это то, что происходит на самом деле. Я определенно видел контроллеры детского вида с ненулевыми topLayoutGuides, но я все еще выясняю причуды. (В моем случае верхний гид должны быть нулевым, так как вид не находится в верхней части экрана, который является то, что я бьюсь головой о в данный момент...)
Это подход для известной длины направляющей. Создайте ограничения не для направляющих, а для верхней части вида с фиксированными константами, предполагая, что расстояние между направляющими будет.
быстрая реализация @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 } }
Не уверен, что у кого-то все еще есть проблемы с этим, как я все еще делал несколько минут назад.
Моя проблема такой (Источник gif от https://knuspermagier.de/2014-fixing-uipageviewcontrollers-top-layout-guide-problems.html).
для краткости, мой pageViewController имеет 3 дочерних viewcontrollers. Первый viewcontroller в порядке, но когда я перехожу к следующему, весь вид неправильно смещен вверх (~20 пикселей, я думаю), но вернется к нормальному после того, как мой палец от экрана.
Я не спал всю ночь, искал решение для этого, но до сих пор не повезло найти. И вдруг мне пришла в голову такая безумная мысль:[pageViewController setViewControllers:@[listViewControllers[1]] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:^(BOOL finished) { }]; [pageViewController setViewControllers:@[listViewControllers[0]] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:^(BOOL finished) { }];
мой listViewControllers имеет 3 дочерних viewcontrollers. Тот, у которого индекс 0, имеет проблему, поэтому я сначала установил его как корень pageviewcontroller, а сразу после этого вернул его к первому контроллеру представления (как я и ожидал). Вуаля, сработало!
надеюсь, что это помогает!
это неудачное поведение, которое, похоже, было исправлено в iOS 11 с помощью обновления API безопасной зоны. Тем не менее, вы всегда будете получать правильное значение от контроллера корневого представления. Например, если вы хотите, чтобы верхняя безопасная высота области pre-iOS 11:
Swift 4
let root = UIApplication.shared.keyWindow!.rootViewController! let topLayoutGuideLength = root.topLayoutGuide.length