【问题标题】:iOS 7: Custom container view controller and content insetiOS 7:自定义容器视图控制器和内容插入
【发布时间】:2013-09-28 08:55:00
【问题描述】:

我有一个封装在导航控制器中的表格视图控制器。当通过presentViewController:animated:completion: 呈现时,导航控制器似乎会自动将正确的内容插入应用到表格视图控制器。 (谁能向我解释一下这是如何工作的?)

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

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

【问题讨论】:

    标签: ios objective-c uiviewcontroller ios7


    【解决方案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://stackoverflow.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 答案的链接。
          猜你喜欢
          • 1970-01-01
          • 2011-05-15
          • 1970-01-01
          • 2014-08-03
          • 2013-10-12
          • 2012-04-19
          • 2015-10-30
          • 2013-10-03
          相关资源
          最近更新 更多