Ich habe ein UIPageViewController
mit durchsichtiger Statusleiste und Navigationsleiste. Sein topLayoutGuide
beträgt erwartungsgemäß 64 Pixel.
Die untergeordneten Ansichtscontroller des UIPageViewController
melden jedoch einen topLayoutGuide
von 0 Pixeln, selbst wenn sie in der Statusleiste und der Navigationsleiste angezeigt werden.
Ist das das erwartete Verhalten? Wenn ja, wie kann eine Ansicht eines untergeordneten Ansichtscontrollers am besten unter dem tatsächlichen topLayoutGuide
positioniert werden?
(kurz vor der Verwendung von parentViewController.topLayoutGuide
, was ich als Hack bezeichnen würde)
Obwohl diese Antwort richtig sein mag, musste ich den Containment-Baum immer noch nach oben durchsuchen, um den richtigen übergeordneten View-Controller zu finden und das zu erhalten, was Sie als "real topLayoutGuide
" bezeichnen. Auf diese Weise kann ich automaticallyAdjustsScrollViewInsets
manuell implementieren.
So mache ich es:
In meinem Table View Controller (eine Unterklasse von UIViewController
) habe ich Folgendes:
- (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;
}
Beachten Sie die Kategoriemethoden in UIViewController
. So habe ich sie implementiert:
@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
Hoffe das hilft :)
sie können dem Storyboard eine Einschränkung hinzufügen und diese in viewWillLayoutSubviews ändern
etwas wie das:
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
self.topGuideConstraint.constant = [self.parentViewController.topLayoutGuide length];
}
Ich könnte mich irren, aber meiner Meinung nach ist das Verhalten korrekt. Der Wert topLayout kann vom Container-Ansichtscontroller zum Layouten der Unteransichten der Ansicht verwendet werden.
Die Referenz sagt:
Um eine obere Layout-Hilfslinie ohne Einschränkungen zu verwenden, ermitteln Sie die Position der Hilfslinie relativ zum oberen Rand der enthaltenen Ansicht.
In der übergeordneten Ansicht ist der Wert relativ zur übergeordneten Ansicht 64.
In dem untergeordneten Element ist der Wert relativ zur übergeordneten Ansicht (der übergeordneten Ansicht) 0.
Im Container View Controller können Sie die Eigenschaft folgendermaßen verwenden:
- (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);
}
}
Der untergeordnete Ansichtscontroller muss nicht wissen, dass es eine Navigations- und eine Statusleiste gibt. Der übergeordnete Ansichtscontroller hat seine Unteransichten bereits so angelegt, dass dies berücksichtigt wird.
Wenn ich ein neues seitenbasiertes Projekt erstelle, es in einen Navigationscontroller einbette und den übergeordneten Ansichtscontrollern diesen Code hinzufüge, funktioniert es anscheinend einwandfrei:
In der Dokumentation wird empfohlen, topLayoutGuide in viewDidLayoutSubviews zu verwenden, wenn Sie eine UIViewController-Unterklasse verwenden, oder layoutSubviews, wenn Sie eine UIView-Unterklasse verwenden.
Wenn Sie es in diesen Methoden verwenden, sollten Sie einen geeigneten Wert ungleich Null erhalten.
Dokumentationslink: https://developer.Apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//Apple_ref/occ/instp/UIViewController/topLayoutGuide
Für den Fall, dass Sie UIPageViewController
haben, wie es OP tut, und Sie zum Beispiel Collection View Controller als Kinder haben. Es hat sich herausgestellt, dass die Korrektur für den Inhaltseintrag einfach ist und unter iOS 8 funktioniert:
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
UIEdgeInsets insets = self.collectionView.contentInset;
insets.top = self.parentViewController.topLayoutGuide.length;
self.collectionView.contentInset = insets;
self.collectionView.scrollIndicatorInsets = insets;
}
Dies wurde in iOS 8 behoben.
So legen Sie die topLayoutGuide-Position für den Child View Controller fest
Grundsätzlich sollte der Container-Ansichts-Controller den (top|bottom|left|right)LayoutGuide
Des untergeordneten Ansichts-Controllers wie jede andere Ansicht einschränken. (In iOS 7 war die Priorität bereits vollständig festgelegt, sodass dies nicht funktionierte.)
Ich denke, die Anleitungen sind auf jeden Fall für verschachtelte untergeordnete Controller gedacht. Angenommen, Sie haben:
Dies ist sinnvoll: Der verschachtelte Ansichtscontroller kann Einschränkungen festlegen, um unerwünschte Überlappungen zu vermeiden, genau wie bei einem Controller der obersten Ebene. Es muss nicht wichtig sein, dass es verschachtelt ist oder wo auf dem Bildschirm das übergeordnete Element es anzeigt, und der übergeordnete Ansichts-Controller muss nicht wissen, wie das untergeordnete Element mit der Statusleiste interagieren möchte.
Das scheint auch so zu sein, wie es in der Dokumentation - oder zumindest in einigen Teilen der Dokumentation - heißt:
In der oberen Layout-Anleitung wird der Abstand in Punkten zwischen dem oberen Rand der Ansicht eines Ansichtscontrollers und dem unteren Rand der untersten Leiste angegeben, die die Ansicht überlagert
Das heißt nicht, dass Sie nur mit View Controllern der obersten Ebene arbeiten müssen.
Aber ich weiß nicht, ob das tatsächlich passiert. Ich habe auf jeden Fall Kinder-View-Controller mit topLayoutGuides ungleich Null gesehen, aber ich finde immer noch die Macken heraus. (In meinem Fall sollte die obere Führung Null sein, da sich die Ansicht nicht oben auf dem Bildschirm befindet Kopf gegen im Moment ...)
Ich bin mir nicht sicher, ob noch jemand Probleme damit hat, so wie ich es vor ein paar Minuten getan habe.
Mein Problem ist so (Quell-GIF von https://knuspermagier.de/2014-fixing-uipageviewcontrollers-top-layout-guide-problems.html =).
Kurz gesagt, mein pageViewController verfügt über 3 untergeordnete ViewController. Der erste Viewcontroller ist in Ordnung, aber wenn ich zum nächsten schiebe, wird die gesamte Ansicht fälschlicherweise nach oben verschoben (~ 20 Pixel, denke ich), wird aber wieder normal, wenn mein Finger vom Bildschirm abweicht.
Ich blieb die ganze Nacht wach und suchte nach einer Lösung, hatte aber immer noch kein Glück, eine zu finden. Dann kam mir plötzlich diese verrückte Idee:
[pageViewController setViewControllers:@[listViewControllers[1]] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:^(BOOL finished) {
}];
[pageViewController setViewControllers:@[listViewControllers[0]] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:^(BOOL finished) {
}];
Meine listViewControllers hat 3 untergeordnete Viewcontroller. Der mit dem Index 0 hat ein Problem, also habe ich ihn zuerst als root von pageviewcontroller festgelegt und gleich danach wieder auf den ersten Viewcontroller zurückgesetzt (wie ich erwartet hatte). Voila, es hat funktioniert!
Ich hoffe es hilft!
Dies ist ein unglückliches Verhalten, das in iOS 11 anscheinend mit der API-Überarbeitung für den sicheren Bereich behoben wurde. Das heißt, Sie erhalten immer den richtigen Wert vom Root-View-Controller. Wenn Sie beispielsweise die Höhe des oberen sicheren Bereichs vor iOS 11 festlegen möchten:
Swift 4
let root = UIApplication.shared.keyWindow!.rootViewController!
let topLayoutGuideLength = root.topLayoutGuide.length
Dies ist der Ansatz für die bekannte Führungslänge. Erstellen Sie Abhängigkeiten nicht für Hilfslinien, sondern für die Ansicht oben mit festen Konstanten, vorausgesetzt, der Hilfslinienabstand beträgt.
Schnelle Implementierung von @NachoSoto Antwort:
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
}
}