web-dev-qa-db-ja.com

子View ControllerのtopLayoutGuide

半透明のステータスバーとナビゲーションバーを備えたUIPageViewControllerがあります。 topLayoutGuideは予想どおり64ピクセルです。

ただし、UIPageViewControllerの子View Controllerは、ステータスバーとナビゲーションバーの下に表示されている場合でも、0ピクセルのtopLayoutGuideを報告します。

これは予想される動作ですか?もしそうなら、実際のtopLayoutGuideの下に子View Controllerのビューを配置する最良の方法は何ですか?

parentViewController.topLayoutGuideを使用するのではなく、ハックと考えます)

69
hpique

この回答 は正しいかもしれませんが、正しい親View Controllerを見つけて「real topLayoutGuide」と記述したものを取得するために、包含ツリーを上に移動する必要があることに気付きました。このようにして、automaticallyAdjustsScrollViewInsetsを手動で実装できます。

これは私がそれをやっている方法です:

私のTable View Controller(実際には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

お役に立てれば :)

48
NachoSoto

ストーリーボードに制約を追加して、viewWillLayoutSubviewsで変更できます

このようなもの:

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    self.topGuideConstraint.constant = [self.parentViewController.topLayoutGuide length];
}
11
chliul

私は間違っているかもしれませんが、私の意見では動作は正しいです。 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 Controllerは、NavigationバーとStatus barがあることを知る必要はありません。親は、それを考慮して既にサブビューをレイアウトしています。

新しいページベースのプロジェクトを作成し、Navigation Controllerに埋め込み、このコードを親View Controllerに追加すると、正常に動作しているようです:

enter image description here

10
vinaut

ドキュメントには、UIViewControllerサブクラスを使用している場合はviewDidLayoutSubviewsでtopLayoutGuideを使用し、UIViewサブクラスを使用している場合はlayoutSubviewsを使用するように記載されています。

これらのメソッドで使用する場合、適切なゼロ以外の値を取得する必要があります。

ドキュメントリンク: https://developer.Apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//Apple_ref/occ/instp/UIViewController/topLayoutGuide

8
CornPuff

OPのようにUIPageViewControllerがあり、たとえばコレクションビューコントローラが子としてある場合。コンテンツのインセットの修正は簡単で、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;
}
3
highmaintenance

これはiOS 8で対処されています。

子View ControllerのtopLayoutGuide位置を設定する方法

基本的に、コンテナView Controllerは、子View Controllerの(top|bottom|left|right)LayoutGuide他のビューと同様。 (iOS 7では、必要な優先度で既に完全に制約されていたため、これは機能しませんでした。)

2
Jesse Rusak

ガイドは、ネストされた子コントローラー用に設定することを確実に意図していると思います。たとえば、次のものがあるとします。

  • 上部に20ピクセルのステータスバーがある100x50の画面。
  • ウィンドウ全体をカバーするトップレベルのView Controller。 topLayoutGuideは20です。
  • 下部の95ピクセルをカバーする上部ビュー内のネストされたビューコントローラー。画面の上部から5ピクセル下。このビューのtopLayoutGuideは15にする必要があります。これは、そのトップ15ピクセルがステータスバーで覆われているためです。

それは理にかなっています。つまり、ネストされたView Controllerは、トップレベルのように、不要なオーバーラップを防ぐために制約を設定できることを意味します。ネストされていることや、親が画面のどこに表示しているかを気にする必要はありません。また、親View Controllerは、子がステータスバーとどのようにやり取りしたいかを知る必要はありません。

それは、ドキュメント、少なくともドキュメントの一部でもあるようです:

上部のレイアウトガイドは、View Controllerのビューの上部と、ビューをオーバーレイする一番下のバーの下部との間の距離をポイント単位で示します

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

トップレベルのView Controllerでのみ機能することについては何も言っていません。

しかし、これが実際に起こるのかどうかはわかりません。 topLayoutGuidesが0以外の子View Controllerを見たことは間違いありませんが、私はまだその癖を理解しています。 (私の場合、ビューが画面の上部にないため、トップガイドshouldはゼロである必要があります。現時点で反対...)

1
Glenn Maynard

私が数分前にまだやったように、誰かがまだこれで問題を抱えているかどうかはわかりません。
私の問題は このように (ソースgifから https://knuspermagier.de/2014-fixing-uipageviewcontrollers-top-layout-guide-problems.html =)。
簡単に言うと、pageViewControllerには3つの子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つの子ViewControllerがあります。インデックス0にあるものには問題があるため、最初にpageviewcontrollerのルートとして設定し、その後すぐに(予想どおり)最初のView Controllerに戻します。出来上がり、うまくいきました!
それが役に立てば幸い!

0
Dat Kin

これは、iOS 11でセーフエリアAPIの改良により修正されたと思われる不幸な動作です。つまり、常にルートビューコントローラーから正しい値を取得します。たとえば、iOS 11より前のセーフエリアの上部の高さが必要な場合:

Swift 4

let root = UIApplication.shared.keyWindow!.rootViewController!
let topLayoutGuideLength = root.topLayoutGuide.length
0
bsod

これは、既知のガイド長のアプローチです。ガイドではなく、ガイド距離が想定される固定定数で上部を表示するための制約を作成します。

0
kirander

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