我有一个包含在导航控制器中的表视图控制器。当通过presentViewController:animated:completion:
呈现时,导航控制器似乎会自动将正确的内容插入应用于表视图控制器。 (任何人都可以向我解释这是如何工作的吗?)
但是,只要我将组合包装在自定义容器视图控制器中并显示该组合,表视图内容的最顶部就会隐藏在导航栏后面。为了在此配置中保留自动内容插入行为,我能做些什么吗?我是否必须在容器视图控制器中“通过”某些内容才能使其正常工作?
我希望避免手动或通过自动布局调整内容,因为我想继续支持iOS 5.
答案 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中的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;。