ios-嵌入到自定义容器视图控制器中时,内容位于导航栏下方。

UPDATE

根据Tim的回答,我在每个具有滚动视图(或子类)的视图控制器中实现了以下内容,该滚动视图是我的自定义容器的一部分:

- (void)didMoveToParentViewController:(UIViewController *)parent
{
    if (parent) {
        CGFloat top = parent.topLayoutGuide.length;
        CGFloat bottom = parent.bottomLayoutGuide.length;

        // this is the most important part here, because the first view controller added 
        // never had the layout issue, it was always the second. if we applied these
        // edge insets to the first view controller, then it would lay out incorrectly.
        // first detect if it's laid out correctly with the following condition, and if
        // not, manually make the adjustments since it seems like UIKit is failing to do so
        if (self.collectionView.contentInset.top != top) {
            UIEdgeInsets newInsets = UIEdgeInsetsMake(top, 0, bottom, 0);
            self.collectionView.contentInset = newInsets;
            self.collectionView.scrollIndicatorInsets = newInsets;
        }
    }

    [super didMoveToParentViewController:parent];
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

我有一个名为SegmentedPageViewController的自定义容器视图控制器。我将其设置为UISegmentedControl

SegmentedPageViewController的目的是允许UISegmentedControl(设置为NavController的titleView)在不同的子视图控制器之间进行切换。

enter image description here

这些子视图控制器都包含滚动视图,表视图或集合视图。

我们发现第一个视图控制器加载正确,并正确定位在导航栏下方。 但是当我们切换到新的视图控制器,导航栏不受尊重,并且视图设置在导航栏下方。

enter image description here

我们正在使用自动布局和界面生成器。 我们已经尝试了所有可以想到的方法,但是找不到一致的解决方案。

这是主要的代码块,负责设置第一个视图控制器,并在用户点击分段控件时切换到另一个视图控制器:

- (void)switchFromViewController:(UIViewController *)oldVC toViewController:(UIViewController *)newVC
{
    if (newVC == oldVC) return;

    // Check the newVC is non-nil otherwise expect a crash: NSInvalidArgumentException
    if (newVC) {

        // Set the new view controller frame (in this case to be the size of the available screen bounds)
        // Calulate any other frame animations here (e.g. for the oldVC)
        newVC.view.frame = self.view.bounds;

        // Check the oldVC is non-nil otherwise expect a crash: NSInvalidArgumentException
        if (oldVC) {
            // **** THIS RUNS WHEN A NEW VC IS SET ****
            // DIFFERENT FROM FIRST VC IN THAT WE TRANSITION INSTEAD OF JUST SETTING


            // Start both the view controller transitions
            [oldVC willMoveToParentViewController:nil];
            [self addChildViewController:newVC];

            // Swap the view controllers
            // No frame animations in this code but these would go in the animations block
            [self transitionFromViewController:oldVC
                              toViewController:newVC
                                      duration:0.25
                                       options:UIViewAnimationOptionLayoutSubviews
                                    animations:^{}
                                    completion:^(BOOL finished) {
                                        // Finish both the view controller transitions
                                        [oldVC removeFromParentViewController];
                                        [newVC didMoveToParentViewController:self];
                                        // Store a reference to the current controller
                                        self.currentViewController = newVC;
                                    }];
        } else {

            // **** THIS RUNS WHEN THE FIRST VC IS SET ****
            // JUST STANDARD VIEW CONTROLLER CONTAINMENT

            // Otherwise we are adding a view controller for the first time
            // Start the view controller transition
            [self addChildViewController:newVC];

            // Add the new view controller view to the view hierarchy
            [self.view addSubview:newVC.view];

            // End the view controller transition
            [newVC didMoveToParentViewController:self];

            // Store a reference to the current controller
            self.currentViewController = newVC;
        }
    }

}
djibouti33 asked 2019-11-18T15:33:14Z
5个解决方案
26 votes

您的自定义容器视图控制器将需要根据您已知的导航栏高度调整第二个视图控制器的contentInset,同时尊重子视图控制器的automaticallyAdjustsScrollViewInsets属性。 (您可能还对容器的topLayoutGuide属性感兴趣-确保在视图切换期间和之后它返回正确的值。)

UIKit在应用此逻辑的方式上非常不一致(且有错误)。 有时,您会看到它到达层次结构中的多个视图控制器,从而自动为您执行此调整,但是通常在自定义容器切换之后,您需要自己完成这项工作。

Tim answered 2019-11-18T15:33:51Z
22 votes

这似乎比人们想象的要简单得多。

UINavigationController仅在布置子视图时才会设置scrollview插入。 addChildViewController:不会引起布局,因此,在调用它之后,您只需要在navigationController上调用setNeedsLayout。 在自定义类似标签的视图中切换视图时,我要做的是:

[self addChildViewController:newcontroller];
[self.view insertSubview:newview atIndex:0];
[self.navigationController.view setNeedsLayout];

最后一行将导致为新视图控制器的内容重新计算滚动视图插图。

Dag Ågren answered 2019-11-18T15:34:33Z
9 votes

仅供参考,以防有人遇到类似问题:即使没有嵌入式视图控制器,也可能发生此问题。 似乎仅当滚动视图(或tableview / collectionview / webview)是其视图控制器层次结构中的第一个视图时,才应用automaticallyAdjustsScrollViewInsets

我经常在我的层次结构中首先添加一个UIImageView以获得背景图像。 如果执行此操作,则必须在viewDidLayoutSubviews中手动设置滚动视图的边缘插图:

- (void) viewDidLayoutSubviews {
    CGFloat top = self.topLayoutGuide.length;
    CGFloat bottom = self.bottomLayoutGuide.length;
    UIEdgeInsets newInsets = UIEdgeInsetsMake(top, 0, bottom, 0);
    self.collectionView.contentInset = newInsets;

}
BFar answered 2019-11-18T15:35:10Z
0 votes

我发现了一个更好的解决方案,使用未公开的UINavigationController方法。

#import <UIKit/UIKit.h>

@interface UINavigationController (ContentInset)


- (void) computeAndApplyScrollContentInsetDeltaForViewController:(UIViewController*) controller;

@end

- (void) cycleFromViewController: (UIViewController*) oldC
                toViewController: (UIViewController*) newC
{
    [oldC willMoveToParentViewController:nil];                        
    [self addChildViewController:newC];
  
    [self transitionFromViewController: oldC toViewController: newC   
                              duration: 0.25 options:0
                            animations:^{
                                newC.view.frame = oldC.view.frame;                                    
                                [self.navigationController computeAndApplyScrollContentInsetDeltaForViewController:newC];
                            }
                            completion:^(BOOL finished) {
                                [oldC removeFromParentViewController];                  
                                [newC didMoveToParentViewController:self];
                            }];
}

然后,像这样

- (void) cycleFromViewController: (UIViewController*) oldC
                toViewController: (UIViewController*) newC
{
    [oldC willMoveToParentViewController:nil];                        
    [self addChildViewController:newC];
  
    [self transitionFromViewController: oldC toViewController: newC   
                              duration: 0.25 options:0
                            animations:^{
                                newC.view.frame = oldC.view.frame;                                    
                                [self.navigationController computeAndApplyScrollContentInsetDeltaForViewController:newC];
                            }
                            completion:^(BOOL finished) {
                                [oldC removeFromParentViewController];                  
                                [newC didMoveToParentViewController:self];
                            }];
}

liaofc answered 2019-11-18T15:36:05Z
0 votes

在我的子控制器上设置edgesForExtendedLayout = []为我工作。

coyer answered 2019-11-18T15:36:36Z
translate from https://stackoverflow.com:/questions/19038949/content-falls-beneath-navigation-bar-when-embedded-in-custom-container-view-cont