Program Tip

자식 뷰 컨트롤러의 topLayoutGuide

programtip 2020. 11. 5. 18:53
반응형

자식 뷰 컨트롤러의 topLayoutGuide


나는이 UIPageViewController반투명 상태 표시 줄 및 탐색 바. 그는 topLayoutGuide예상대로 64 픽셀이다.

그러나 UIPageViewController보고서 의 하위보기 컨트롤러 topLayoutGuide는 상태 표시 줄과 탐색 표시 줄 아래에 표시 되더라도 0 픽셀입니다.

이것이 예상되는 동작입니까? 그렇다면 자식 뷰 컨트롤러의 뷰를 real 아래에 배치하는 가장 좋은 방법은 무엇 topLayoutGuide입니까?

(사용이 부족하여 parentViewController.topLayoutGuide해킹이라고 생각합니다)


이 대답 이 정확할 수도 있지만 , 올바른 상위 뷰 컨트롤러를 찾고 "실제 topLayoutGuide" 라고 설명하는 것을 얻기 위해 격리 트리를 위로 이동해야합니다 . 이렇게하면 수동으로 automaticallyAdjustsScrollViewInsets.

이것이 내가하는 방법입니다.

내 테이블 뷰 컨트롤러 ( 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이됩니다.

컨테이너 뷰 컨트롤러에서 다음과 같이 속성을 사용할 수 있습니다.

- (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가 있다는 것을 알 필요가 없습니다. 부모는 이미 그것을 고려하여 하위 view를 배치했을 것입니다.

새 페이지 기반 프로젝트를 만들고 탐색 컨트롤러에 포함하고이 코드를 상위 뷰 컨트롤러에 추가하면 제대로 작동하는 것 같습니다.

여기에 이미지 설명 입력


설명서에 따르면 UIViewController 하위 클래스를 사용하는 경우 viewDidLayoutSubviews에서 topLayoutGuide를 사용하고 UIView 하위 클래스를 사용하는 경우 layoutSubviews를 사용합니다.

이러한 메서드에서 사용하는 경우 적절한 0이 아닌 값을 얻어야합니다.

문서 링크 : https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instp/UIViewController/topLayoutGuide


In case if you have UIPageViewController like OP does and you have for example collection view controllers as children. Turns out the fix for content inset is simple and it works on 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;
}

This has been addressed in iOS 8.

How to set topLayoutGuide position for child view controller

Essentially, the container view controller should constrain the child view controller's (top|bottom|left|right)LayoutGuide as it would any other view. (In iOS 7, it was already fully constrained at a required priority, so this didn't work.)


I think the guides are definitely meant to be set for nested child controllers. For example, suppose you have:

  • A 100x50 screen, with a 20 pixel status bar at the top.
  • A top-level view controller, covering the whole window. Its topLayoutGuide is 20.
  • A nested view controller inside the top view covering the bottom 95 pixels, eg. 5 pixels down from the top of the screen. This view should have a topLayoutGuide of 15, since its top 15 pixels are covered by the status bar.

That would make sense: it means that the nested view controller can set constraints to prevent unwanted overlap, just like a top-level one. It doesn't have to care that it's nested, or where on the screen its parent is displaying it, and the parent view controller doesn't need to know how the child wants to interact with the status bar.

That also seems to be what the documentation--or some of the documentation, at least--says:

The top layout guide indicates the distance, in points, between the top of a view controller’s view and the bottom of the bottommost bar that overlays the view

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

That doesn't say anything about only working for top-level view controllers.

But, I don't know if this is what actually happens. I've definitely seen child view controllers with nonzero topLayoutGuides, but I'm still figuring out the quirks. (In my case the top guide should be zero, since the view isn't at the top of the screen, which is what I'm banging my head against at the moment...)


This is the approach for the known guide length. Create constrains not to guides, but to view's top with fixed constants assuming guide distance will be.


Swifty implementation of @NachoSoto answer:

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

Not sure if anyone still got problem with this, as I still did a few minutes ago.
My problem is like this (source gif from https://knuspermagier.de/2014-fixing-uipageviewcontrollers-top-layout-guide-problems.html).
For short, my pageViewController has 3 child viewcontrollers. First viewcontroller is fine, but when I slide to the next one, the whole view is incorrectly offset to the top (~20 pixel, I guess), but will return to normal after my finger is off the screen.
I stayed up all night looking for solution for this but still no luck finding one. Then suddenly I came up with this crazy idea:

[pageViewController setViewControllers:@[listViewControllers[1]] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:^(BOOL finished) {

}];

[pageViewController setViewControllers:@[listViewControllers[0]] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:^(BOOL finished) {

}];

내 listViewControllers에는 3 개의 자식 뷰 컨트롤러가 있습니다. 인덱스 0에있는 하나에 문제가 있으므로 먼저 pageviewcontroller의 루트로 설정하고 그 직후에 예상대로 첫 번째 뷰 컨트롤러로 다시 설정합니다. 짜잔, 작동했습니다!
도움이 되었기를 바랍니다.


이것은 안전 영역 API 개편으로 iOS 11에서 수정 된 것으로 보이는 불행한 동작입니다. 즉, 루트 뷰 컨트롤러에서 항상 올바른 값을 얻을 수 있습니다. 예를 들어 iOS 11 이전의 상위 안전 영역 높이를 원하는 경우

스위프트 4

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

참고 URL : https://stackoverflow.com/questions/19140530/toplayoutguide-in-child-view-controller

반응형