我有一个包裹在导航控制器中的表格视图控制器。导航控制器似乎会在通过presentViewController:animated:completion:
. (谁能向我解释这到底是如何工作的?)
但是,只要我将组合包装在自定义容器视图控制器中并呈现它,表格视图内容的最顶部就会隐藏在导航栏后面。为了在此配置中保留自动内容插入行为,我能做些什么吗?我是否必须在容器视图控制器中“通过”某些东西才能正常工作?
我想避免手动或通过自动布局调整内容插图,因为我想继续支持 iOS 5。
我有一个包裹在导航控制器中的表格视图控制器。导航控制器似乎会在通过presentViewController:animated:completion:
. (谁能向我解释这到底是如何工作的?)
但是,只要我将组合包装在自定义容器视图控制器中并呈现它,表格视图内容的最顶部就会隐藏在导航栏后面。为了在此配置中保留自动内容插入行为,我能做些什么吗?我是否必须在容器视图控制器中“通过”某些东西才能正常工作?
我想避免手动或通过自动布局调整内容插图,因为我想继续支持 iOS 5。
我有一个类似的问题。所以在 iOS 7 上,在UIViewController
called上有一个新属性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 兼容性时,我真的没有看到其他方法。
仅供参考,以防有人遇到类似问题:似乎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;
}
感谢 Johannes Fahrenkrug 的提示,我发现了以下内容:automaticallyAdjustsScrollViewInsets
确实似乎只有在子视图控制器的根视图是UIScrollView
. 对我来说,这意味着以下安排有效:
自定义容器视图控制器内的导航控制器内的内容视图控制器。
虽然这不是:
导航控制器内的自定义容器视图控制器内的内容视图控制器。
然而,从逻辑的角度来看,第二种选择似乎更明智。第一个选项可能仅适用,因为自定义容器视图控制器用于将视图附加到内容的底部。如果我想把视图放在导航栏和内容之间,那是行不通的。
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];
}];
您不需要调用任何未记录的方法。您需要做的就是调用setNeedsLayout
您的UINavigationController
. 请参阅其他答案:https ://stackoverflow.com/a/33344516/5488931
此解决方案针对 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”问题的回答的帮助下弄清楚了这一点。