Ich habe eine UIPageViewController
mit durchscheinende Statusleiste und Navigationsleiste. Es topLayoutGuide
ist wie erwartet 64 Pixel.
Die untergeordneten Ansichtssteuerungen des UIPageViewController
Berichts haben jedoch eine topLayoutGuide
Größe von 0 Pixel, selbst wenn sie in der Statusleiste und in der Navigationsleiste angezeigt werden.
Ist das das erwartete Verhalten? Wenn ja, wie lässt sich eine Ansicht eines untergeordneten Ansichtscontrollers am besten unter der realen Position positionieren topLayoutGuide
?
(kurz vor der Verwendung parentViewController.topLayoutGuide
, was ich als Hack betrachten würde)
topLayoutGuide
gründlich genug für verschachtelte ViewController-Containments implementiert wurden. Wirft die Frage auf, wie man damit umgehen könnte, wenn ein benutzerdefinierter Container implementiert wird ...viewWillLayoutSubviews
, ja.Antworten:
Obwohl diese Antwort möglicherweise richtig ist, musste ich den Containment-Baum nach oben verschieben, um den richtigen übergeordneten Ansichts-Controller zu finden und das zu erhalten, was Sie als "real
topLayoutGuide
" bezeichnen. Auf diese Weise kann ich manuell implementierenautomaticallyAdjustsScrollViewInsets
.So mache ich es:
In meinem Table View Controller (eine Unterklasse von
UIViewController
tatsächlich) 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 :)
quelle
[_tableView setContentOffset:CGPointMake(0, 0 - _tableView.contentInset.top) animated:NO];
, damit es richtig nach oben scrollen konnteIch könnte mich irren, aber meiner Meinung nach ist das Verhalten korrekt. Der Wert topLayout kann vom Container View Controller verwendet werden, um die Unteransichten seiner Ansicht zu gestalten.
Die Referenz sagt:
Um eine Top-Layout-Anleitung ohne Verwendung von Einschränkungen zu verwenden, ermitteln Sie die Position der Anleitung relativ zur oberen Grenze der enthaltenen Ansicht .
Im übergeordneten Element beträgt der Wert relativ zur enthaltenen Ansicht 64.
Im untergeordneten Element ist der Wert relativ zur enthaltenen Ansicht (dem übergeordneten Element) 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: Das übergeordnete Element hat seine Unteransichten unter Berücksichtigung dieser bereits angelegt.
Wenn ich ein neues seitenbasiertes Projekt erstelle, es in einen Navigationscontroller einbetten und diesen Code zu den übergeordneten Ansichtscontrollern hinzufügen, scheint es einwandfrei zu funktionieren:
quelle
UIScrollView
Unterklassen handelt, wird der Inhalt nicht unter der durchscheinenden Navigationsleiste angezeigt, wenn Sie den Rahmen so einstellen. Dies ist der ganze Zweck einer durchscheinenden Navigationsleiste .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]; }
quelle
override func viewWillLayoutSubviews() {self.topGuideConstraint.constant = (self.parent ?? self).topLayoutGuide.length}
In der Dokumentation wird angegeben, dass topLayoutGuide in viewDidLayoutSubviews verwendet werden soll, 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
quelle
Für den Fall, dass Sie
UIPageViewController
wie OP haben und Sie zum Beispiel Sammlungsansichts-Controller als Kinder haben. Es stellt sich heraus, dass die Korrektur für das Einfügen von Inhalten 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; }
quelle
Dies wurde in iOS 8 behoben.
So legen Sie die Position topLayoutGuide für den untergeordneten Ansichtscontroller fest
Im Wesentlichen sollte der Container-Ansichts-Controller den untergeordneten Ansichts-Controller
(top|bottom|left|right)LayoutGuide
wie jede andere Ansicht einschränken . (In iOS 7 war die erforderliche Priorität bereits vollständig eingeschränkt, sodass dies nicht funktionierte.)quelle
Ich denke, die Anleitungen sind definitiv für verschachtelte untergeordnete Controller gedacht. Angenommen, Sie haben:
Das wäre sinnvoll: Dies bedeutet, dass der Controller für verschachtelte Ansichten Einschränkungen festlegen kann, 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 es auf dem Bildschirm von seinem übergeordneten Element angezeigt wird, und der übergeordnete Ansichts-Controller muss nicht wissen, wie das untergeordnete Element mit der Statusleiste interagieren möchte.
Das scheint auch das zu sein, was die Dokumentation - oder zumindest ein Teil der Dokumentation - sagt:
( https://developer.apple.com/library/ios/documentation/UIKit/Reference/UILayoutSupport_Protocol/Reference/Reference.html )
Das sagt nichts darüber aus, nur für Top-Level-View-Controller zu arbeiten.
Aber ich weiß nicht, ob das tatsächlich passiert. Ich habe definitiv untergeordnete View-Controller mit topLayoutGuides ungleich Null gesehen, aber ich finde immer noch die Macken heraus. (In meinem Fall ist die obere Führung sollte Null sein, da die Ansicht nicht am oberen Rand des Bildschirms ist, das ist , was ich schlug meinen Kopf gegen zur Zeit ...)
quelle
Dies ist der Ansatz für die bekannte Führungslänge. Erstellen Sie Einschränkungen nicht für Hilfslinien, sondern für die Ansicht oben mit festen Konstanten unter der Annahme, dass die Führungsentfernung gleich ist.
quelle
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 } }
quelle
Ich bin mir nicht sicher, ob noch jemand ein Problem damit hat, wie ich es noch vor ein paar Minuten getan habe.
Mein Problem ist wie folgt (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 Ansichtscontroller ist in Ordnung, aber wenn ich zum nächsten schiebe, ist die gesamte Ansicht falsch nach oben versetzt (~ 20 Pixel, denke ich), wird aber wieder normal, nachdem mein Finger vom Bildschirm entfernt ist.
Ich blieb die ganze Nacht wach und suchte nach einer Lösung dafür, 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 listViewController haben 3 untergeordnete Ansichtscontroller. Der am Index 0 hat ein Problem, daher habe ich ihn zuerst als Stammverzeichnis des Seitenaufrufcontrollers festgelegt und gleich danach (wie erwartet) wieder auf den ersten Ansichtscontroller zurückgesetzt. Voila, es hat funktioniert!
Ich hoffe es hilft!
quelle
Dies ist ein unglückliches Verhalten, das in iOS 11 mit der API-Überarbeitung für den sicheren Bereich behoben worden zu sein scheint. Das heißt, Sie erhalten immer den richtigen Wert vom Root-View-Controller. Wenn Sie beispielsweise die obere Höhe des sicheren Bereichs vor iOS 11 möchten:
Swift 4
let root = UIApplication.shared.keyWindow!.rootViewController! let topLayoutGuideLength = root.topLayoutGuide.length
quelle