24

我有一个包裹在导航控制器中的表格视图控制器。导航控制器似乎会在通过presentViewController:animated:completion:. (谁能向我解释这到底是如何工作的?)

但是,只要我将组合包装在自定义容器视图控制器中并呈现它,表格视图内容的最顶部就会隐藏在导航栏后面。为了在此配置中保留自动内容插入行为,我能做些什么吗?我是否必须在容器视图控制器中“通过”某些东西才能正常工作?

我想避免手动或通过自动布局调整内容插图,因为我想继续支持 iOS 5。

4

6 回答 6

12

我有一个类似的问题。所以在 iOS 7 上,在UIViewControllercalled上有一个新属性automaticallyAdjustsScrollViewInsets。默认情况下,此设置为YES。当您的视图层次结构比导航或标签栏控制器内的滚动/表格视图更复杂时,此属性似乎对我不起作用。

我所做的是:我创建了一个基本视图控制器类,我的所有其他视图控制器都继承自该类。然后,该视图控制器负责显式设置插图:

标题:

#import <UIKit/UIKit.h>

@interface SPWKBaseCollectionViewController : UICollectionViewController

@end

执行:

#import "SPWKBaseCollectionViewController.h"

@implementation SPWKBaseCollectionViewController


- (void)viewDidLoad
{
    [super viewDidLoad];

    [self updateContentInsetsForInterfaceOrientation:self.interfaceOrientation];
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self updateContentInsetsForInterfaceOrientation:toInterfaceOrientation];

    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}

- (void)updateContentInsetsForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
        UIEdgeInsets insets;

        if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
            insets = UIEdgeInsetsMake(64, 0, 56, 0);
        } else if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
            if (UIInterfaceOrientationIsPortrait(orientation)) {
                insets = UIEdgeInsetsMake(64, 0, 49, 0);
            } else {
                insets = UIEdgeInsetsMake(52, 0, 49, 0);
            }
        }

        self.collectionView.contentInset = insets;
        self.collectionView.scrollIndicatorInsets = insets;
    }
}

@end

这也适用于网络视图:

self.webView.scrollView.contentInset = insets;
self.webView.scrollView.scrollIndicatorInsets = insets;

如果有更优雅但更可靠的方法来做到这一点,请告诉我!硬编码的插入值闻起来很糟糕,但是当您想要保持 iOS 5 兼容性时,我真的没有看到其他方法。

于 2013-10-08T18:54:06.860 回答
5

仅供参考,以防有人遇到类似问题:似乎automaticallyAdjustsScrollViewInsets仅当您的滚动视图(或 tableview/collectionview/webview)是其视图控制器层次结构中的第一个视图时才适用。

我经常在我的层次结构中首先添加一个 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;

}
于 2014-08-17T18:36:09.377 回答
4

感谢 Johannes Fahrenkrug 的提示,我发现了以下内容:automaticallyAdjustsScrollViewInsets确实似乎只有在子视图控制器的根视图是UIScrollView. 对我来说,这意味着以下安排有效:

自定义容器视图控制器内的导航控制器内的内容视图控制器。

虽然这不是:

导航控制器内的自定义容器视图控制器内的内容视图控制器。

然而,从逻辑的角度来看,第二种选择似乎更明智。第一个选项可能仅适用,因为自定义容器视图控制器用于将视图附加到内容的底部。如果我想把视图放在导航栏和内容之间,那是行不通的。

于 2013-10-20T15:13:23.147 回答
3

UINavigationController 在控制器之间的转换中调用未记录的方法 _updateScrollViewFromViewController:toViewController 以更新内容插图。这是我的解决方案:

UINavigationController+ContentInset.h

#import <UIKit/UIKit.h>
@interface UINavigationController (ContentInset)
- (void) updateScrollViewFromViewController:(UIViewController*) from toViewController:(UIViewController*) to;
@end

UINavigationController+ContentInset.m

#import "UINavigationController+ContentInset.h"
@interface UINavigationController()
- (void) _updateScrollViewFromViewController:(UIViewController*) from toViewController:(UIViewController*) to;
@end

@implementation UINavigationController (ContentInset)
- (void) updateScrollViewFromViewController:(UIViewController*) from toViewController:(UIViewController*) to {
    if ([UINavigationController instancesRespondToSelector:@selector(_updateScrollViewFromViewController:toViewController:)])
        [self _updateScrollViewFromViewController:from toViewController:to];
}
@end

在自定义容器视图控制器中的某处调用 updateScrollViewFromViewController:toViewController

[self addChildViewController:newContentViewController];
[self transitionFromViewController:previewsContentViewController
                  toViewController:newContentViewController
                          duration:0.5f
                           options:0
                        animations:^{
                            [self.navigationController updateScrollViewFromViewController:self toViewController:newContentViewController];

                    }
                    completion:^(BOOL finished) {
                        [newContentViewController didMoveToParentViewController:self];
                    }];
于 2014-01-17T12:01:07.570 回答
3

您不需要调用任何未记录的方法。您需要做的就是调用setNeedsLayout您的UINavigationController. 请参阅其他答案:https ://stackoverflow.com/a/33344516/5488931

于 2015-10-26T11:26:53.997 回答
2

迅捷解决方案

此解决方案针对 Swift 中的 iOS 8 及更高版本。

我的应用程序的根视图控制器是一个导航控制器。最初,此导航控制器UIViewController在其堆栈中有一个,我将其称为父视图控制器。

当用户点击导航栏按钮时,父视图控制器使用包含 API 在其视图上的两个子视图控制器之间切换(在映射结果和列出的结果之间切换)。父视图控制器持有对两个子视图控制器的引用。直到用户第一次点击切换按钮时,才会创建第二个子视图控制器。第二个子视图控制器是一个表格视图控制器,无论其automaticallyAdjustsScrollViewInsets属性是如何设置的,它都会出现在导航栏下方的问题。

为了解决这个问题,我在创建子表视图控制器后并在父视图控制器的方法中调用adjustChild:tableView:insetTop(如下所示) 。viewDidLayoutSubviews

子视图控制器的表视图和父视图控制器的topLayoutGuide.length被传递给这样的adjustChild:tableView:insetTop方法......

// called right after childViewController is created
adjustChild(childViewController.tableView, insetTop: topLayoutGuide.length)

adjustChild 方法...

private func adjustChild(tableView: UITableView, insetTop: CGFloat) {
    tableView.contentInset.top = insetTop
    tableView.scrollIndicatorInsets.top = insetTop

    // make sure the tableview is scrolled at least as far as insetTop
    if (tableView.contentOffset.y > -insetTop) {
        tableView.contentOffset.y = -insetTop
    }
}

该语句tableView.scrollIndicatorInsets.top = insetTop调整表格视图右侧的滚动指示器,使其在导航栏下方开始。这是微妙的,很容易被忽视,直到你意识到它。

以下是viewDidLayoutSubviews外观...

override func viewDidLayoutSubviews() {
    if let childViewController = childViewController {
        adjustChild(childViewController.tableView, insetTop: topLayoutGuide.length)
    }
}

请注意,上面的所有代码都出现在父视图控制器中。

我在Christopher Pickslay对“iOS 7 Table view failed to autoadjust content inset”问题的回答的帮助下弄清楚了这一点。

于 2015-10-15T18:25:11.793 回答