webentwicklung-frage-antwort-db.com.de

topLayoutGuide im untergeordneten Ansichtscontroller

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)

69
hpique

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 :)

48
NachoSoto

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];
}
11
chliul

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:

enter image description here

10
vinaut

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

8
CornPuff

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;
}
3
highmaintenance

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.)

2
Jesse Rusak

Ich denke, die Anleitungen sind auf jeden Fall für verschachtelte untergeordnete Controller gedacht. Angenommen, Sie haben:

  • Ein 100x50-Bildschirm mit einer 20-Pixel-Statusleiste oben.
  • Ein View-Controller der obersten Ebene, der das gesamte Fenster abdeckt. Sein topLayoutGuide ist 20.
  • Ein verschachtelter Ansichtscontroller in der Draufsicht, der die unteren 95 Pixel abdeckt, z. 5 Pixel vom oberen Bildschirmrand entfernt. Diese Ansicht sollte einen topLayoutGuide von 15 haben, da die oberen 15 Pixel in der Statusleiste verdeckt sind.

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

( https://developer.Apple.com/library/ios/documentation/UIKit/Reference/UILayoutSupport_Protocol/Reference/Reference.html )

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 ...)

1
Glenn Maynard

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!

0
Dat Kin

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
0
bsod

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.

0
kirander

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
    }
}
0
Vive