半透明のステータスバーとナビゲーションバーを備えたUIPageViewController
があります。 topLayoutGuide
は予想どおり64ピクセルです。
ただし、UIPageViewController
の子View Controllerは、ステータスバーとナビゲーションバーの下に表示されている場合でも、0ピクセルのtopLayoutGuide
を報告します。
これは予想される動作ですか?もしそうなら、実際のtopLayoutGuide
の下に子View Controllerのビューを配置する最良の方法は何ですか?
(parentViewController.topLayoutGuide
を使用するのではなく、ハックと考えます)
この回答 は正しいかもしれませんが、正しい親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
お役に立てれば :)
ストーリーボードに制約を追加して、viewWillLayoutSubviewsで変更できます
このようなもの:
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
self.topGuideConstraint.constant = [self.parentViewController.topLayoutGuide length];
}
私は間違っているかもしれませんが、私の意見では動作は正しいです。 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に追加すると、正常に動作しているようです:
ドキュメントには、UIViewControllerサブクラスを使用している場合はviewDidLayoutSubviewsでtopLayoutGuideを使用し、UIViewサブクラスを使用している場合はlayoutSubviewsを使用するように記載されています。
これらのメソッドで使用する場合、適切なゼロ以外の値を取得する必要があります。
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;
}
これはiOS 8で対処されています。
子View ControllerのtopLayoutGuide位置を設定する方法
基本的に、コンテナView Controllerは、子View Controllerの(top|bottom|left|right)LayoutGuide
他のビューと同様。 (iOS 7では、必要な優先度で既に完全に制約されていたため、これは機能しませんでした。)
ガイドは、ネストされた子コントローラー用に設定することを確実に意図していると思います。たとえば、次のものがあるとします。
それは理にかなっています。つまり、ネストされたView Controllerは、トップレベルのように、不要なオーバーラップを防ぐために制約を設定できることを意味します。ネストされていることや、親が画面のどこに表示しているかを気にする必要はありません。また、親View Controllerは、子がステータスバーとどのようにやり取りしたいかを知る必要はありません。
それは、ドキュメント、少なくともドキュメントの一部でもあるようです:
上部のレイアウトガイドは、View Controllerのビューの上部と、ビューをオーバーレイする一番下のバーの下部との間の距離をポイント単位で示します
トップレベルのView Controllerでのみ機能することについては何も言っていません。
しかし、これが実際に起こるのかどうかはわかりません。 topLayoutGuidesが0以外の子View Controllerを見たことは間違いありませんが、私はまだその癖を理解しています。 (私の場合、ビューが画面の上部にないため、トップガイドshouldはゼロである必要があります。現時点で反対...)
私が数分前にまだやったように、誰かがまだこれで問題を抱えているかどうかはわかりません。
私の問題は このように (ソース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に戻します。出来上がり、うまくいきました!
それが役に立てば幸い!
これは、iOS 11でセーフエリアAPIの改良により修正されたと思われる不幸な動作です。つまり、常にルートビューコントローラーから正しい値を取得します。たとえば、iOS 11より前のセーフエリアの上部の高さが必要な場合:
Swift 4
let root = UIApplication.shared.keyWindow!.rootViewController!
let topLayoutGuideLength = root.topLayoutGuide.length
これは、既知のガイド長のアプローチです。ガイドではなく、ガイド距離が想定される固定定数で上部を表示するための制約を作成します。
@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
}
}