iOS 7:自定义容器视图控制器和内容插入
Posted
技术标签:
【中文标题】iOS 7:自定义容器视图控制器和内容插入【英文标题】:iOS 7: Custom container view controller and content inset 【发布时间】:2013-09-28 08:55:00 【问题描述】:我有一个封装在导航控制器中的表格视图控制器。当通过presentViewController:animated:completion:
呈现时,导航控制器似乎会自动将正确的内容插入应用到表格视图控制器。 (谁能向我解释一下这是如何工作的?)
但是,只要我将组合包装在自定义容器视图控制器中并呈现它,表格视图内容的最顶部就会隐藏在导航栏后面。为了在此配置中保留自动内容插入行为,我能做些什么吗?我是否必须“通过”容器视图控制器中的某些内容才能使其正常工作?
我想避免手动或通过自动布局调整内容插入,因为我想继续支持 ios 5。
【问题讨论】:
【参考方案1】:我也遇到过类似的问题。所以在 iOS 7 上,UIViewController
上有一个新属性,称为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 兼容性时,我真的没有看到其他方法。
【讨论】:
我在类似的情况下正在做这样的事情。不幸的是,如果您有一个用于 tableview 的 UIRefreshControl 并在刷新期间旋转设备,则它不起作用。这似乎会自行更新插图 - 在这种情况下是错误的。 你说得对,我也不喜欢硬编码的插入值。这是否可以使用自动布局来完成,因为它仅适用于 iOS 7?约束是什么样的,它会进入哪个控制器类的哪个方法? @tajmahal 我绝对同意:我也真的不喜欢硬编码的值。就像我说的,当想要保持 iOS 5 兼容性时,我找不到其他方法。如果您找到更清洁的解决方案,请务必分享。自动布局不适用于插图,因为不能使用自动布局调整插图。【参考方案2】:仅供参考,以防有人遇到类似问题:似乎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;
【讨论】:
【参考方案3】:感谢 Johannes Fahrenkrug 的提示,我发现了以下内容:automaticallyAdjustsScrollViewInsets
确实似乎只有在子视图控制器的根视图是 UIScrollView
时才能像宣传的那样工作。对我来说,这意味着以下安排有效:
内容视图控制器在导航控制器在自定义容器视图控制器内。
虽然不是这样:
内容视图控制器在自定义容器视图控制器内导航控制器。
然而,从逻辑的角度来看,第二种选择似乎更明智。第一个选项可能只起作用,因为自定义容器视图控制器用于将视图附加到内容的底部。如果我想把视图放在导航栏和内容之间,那是行不通的。
【讨论】:
【参考方案4】: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];
];
【讨论】:
这仍然是 IOS9beta 的一个问题。这个解决方案对我来说非常有效。对于每个也有这个问题的人。请为该苹果提交一份雷达文件,公开此方法。 这对我来说适用于 iOS 8.4。目前在 iOS 9B5 上,这已停止工作。任何猜测为什么?该方法确实存在,如此处所述github.com/JaviSoto/iOS9-Runtime-Headers/blob/master/Frameworks/… 使用- (void)_computeAndApplyScrollContentInsetDeltaForViewController:(id)arg1;
对我来说适用于 iOS 8 和 9。【参考方案5】:
您不需要调用任何未记录的方法。您需要做的就是在您的UINavigationController
上拨打setNeedsLayout
。查看其他答案:https://***.com/a/33344516/5488931
【讨论】:
【参考方案6】:迅捷解决方案
此解决方案针对 Swift 中的 iOS 8 及更高版本。
我的应用的根视图控制器是一个导航控制器。最初,这个导航控制器的堆栈上有一个UIViewController
,我将其称为父视图控制器。
当用户点击导航栏按钮时,父视图控制器使用包含 API 在其视图上的两个子视图控制器之间切换(在映射结果和列出的结果之间切换)。父视图控制器持有对两个子视图控制器的引用。在用户第一次点击切换按钮之前,不会创建第二个子视图控制器。第二个子视图控制器是一个表格视图控制器,并且无论其automaticallyAdjustsScrollViewInsets
属性是如何设置的,都会出现它在导航栏下方的问题。
为了解决这个问题,我在创建子表视图控制器后并在父视图控制器的 viewDidLayoutSubviews
方法中调用 adjustChild:tableView:insetTop
(如下所示)。
子视图控制器的表格视图和父视图控制器的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 的帮助下解决了这个问题“iOS 7 Table view failed to autoadjust content inset”。
【讨论】:
你有“Christopher Pickslay 的帖子”的链接吗? 嗨@Koen。我在答案底部添加了指向 Christoper Pickslay 答案的链接。以上是关于iOS 7:自定义容器视图控制器和内容插入的主要内容,如果未能解决你的问题,请参考以下文章