iOS 7:自定义容器视图控制器和内容插入

时间:2013-09-28 08:55:00

标签: ios objective-c uiviewcontroller ios7

我有一个包含在导航控制器中的表视图控制器。当通过presentViewController:animated:completion:呈现时,导航控制器似乎会自动将正确的内容插入应用于表视图控制器。 (任何人都可以向我解释这是如何工作的吗?)

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

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

6 个答案:

答案 0 :(得分:12)

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

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

标题

#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的兼容性时,我真的没有看到另一种方式。

答案 1 :(得分:5)

如果有人遇到类似的问题,我们会看到automaticallyAdjustsScrollViewInsets仅在您的滚动视图(或tableview / collectionview / webview)是其视图控制器层次结构中的第一个视图时才会应用。 / p>

我经常在我的层次结构中添加UIImageView以获得背景图像。如果这样做,您必须在viewDidLayoutSubviews中手动设置scrollview的边缘插入内容:

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

}

答案 2 :(得分:4)

感谢Johannes Fahrenkrug的提示,我想出了以下内容:automaticallyAdjustsScrollViewInsets当子视图控制器的根视图为UIScrollView时,确实只能像宣传的那样工作。对我来说,这意味着以下安排有效:

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

内容视图控制器

虽然这不是:

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

内容视图控制器

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

答案 3 :(得分:3)

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

<强>的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];
                    }];

答案 4 :(得分:3)

您不需要调用任何未记录的方法。您只需在setNeedsLayout上致电UINavigationController即可。请参阅其他答案:https://stackoverflow.com/a/33344516/5488931

答案 5 :(得分:2)

Swift Solution

此解决方案针对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's answer的帮助下解决了这个问题&#34; iOS 7表格视图无法自动调整内容插入&#34;。