【问题标题】:UIPageViewController prevents my table view from scrolling to top when tapping the status barUIPageViewController 在点击状态栏时阻止我的表格视图滚动到顶部
【发布时间】:2014-01-09 23:18:22
【问题描述】:

我在发布此问题之前进行了搜索,但找不到任何内容。

我遇到了一个大问题。我有一个滚动类型 UIPageViewController 作为我的应用程序的基础,有 3 个视图控制器。

其中一个视图控制器 (listTableView) 有一个表格视图和一个搜索显示控制器。

问题是,当像普通表格视图一样点击状态栏时,我无法滚动到表格视图的顶部。我相信 UIPageViewController 会干扰此问题,但我不知道如何修复它,但我知道我需要让我的应用程序不会感到损坏。

感谢您提供的任何帮助。

我知道有人会要求提供代码,即使在这种情况下它是无关紧要的,但这里是用于创建 UIPageViewController:

#import "MainViewController.h"

@interface MainViewController ()

@property (nonatomic, strong) UIPageViewController *pageViewController;
@property (nonatomic, strong) NSArray *contentViewControllers;

@end

@implementation MainViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"PageView"];
    self.pageViewController.dataSource = self;

    UIViewController *settings = [self.storyboard instantiateViewControllerWithIdentifier:@"Settings"];

    UIViewController *listTableView = [self.storyboard instantiateViewControllerWithIdentifier:@"List"];

    UIViewController *first = [self.storyboard instantiateViewControllerWithIdentifier:@"First"];

    self.contentViewControllers = [NSArray arrayWithObjects:settings,listTableView,first,nil];

    [self.pageViewController setViewControllers:@[first] direction:UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];

    [self addChildViewController:self.pageViewController];
    [self.view addSubview:self.pageViewController.view];
    [self.pageViewController didMoveToParentViewController:self];

    self.view.backgroundColor = [UIColor colorWithRed:(248.0/255.0) green:(248.0/255.0) blue:(248.0/255.0) alpha:1.0];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
  viewControllerBeforeViewController:(UIViewController *)viewController {

    NSUInteger index = [self.contentViewControllers indexOfObject:viewController];

    if (index == 0) {
        return nil;
    }

    return self.contentViewControllers[index - 1];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
   viewControllerAfterViewController:(UIViewController *)viewController {

    NSUInteger index = [self.contentViewControllers indexOfObject:viewController];

    if (index >= self.contentViewControllers.count - 1) {
        return nil;
    }

    return self.contentViewControllers[index + 1];
}

【问题讨论】:

  • 您是否尝试将 添加到您的 MainViewController 界面?
  • 是的,我有,但这不起作用。问题是您根本无法访问 UIPageViewController 不幸包含并破坏一切的 _UIQueuingScrollView。
  • 我已按照您对应用程序的描述,并用您的代码重新实现了它。它行为正确,我无法复制您的问题。问题一定出在应用程序的其他地方——你能提供更多信息吗?也许你的“列表”视图控制器中的一些代码。
  • 你能显示 MainViewController 接口的声明吗? (MainViewController.h)

标签: ios objective-c uitableview uisearchdisplaycontroller uipageviewcontroller


【解决方案1】:

解决方案

聊天后,我们发现了一些有趣的事情。页面视图控制器将其他视图控制器的视图保留在视图层次结构中,因此它们也会捕获scrollsToTop 行为并进行干预。这意味着您需要为消失的视图控制器的viewWillDisappear: 内的每个可滚动视图禁用scrollsToTop(并在viewWillAppear: 上再次启用)。


原始调查

快速简单的方法:滚动视图是UIPageViewController的视图的唯一子视图:

self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];

在调试器中检查:

(lldb) po [self.pageViewController.view recursiveDescription]
<_UIPageViewControllerContentView: 0x8d7c390; frame = (0 0; 320 480); clipsToBounds = YES; opaque = NO; autoresize = W+H; layer = <CALayer: 0x8d7c4a0>>
   | <_UIQueuingScrollView: 0xa912800; frame = (0 0; 320 480); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x8d7cc90>; layer = <CALayer: 0x8d7c7e0>; contentOffset: {320, 0}>
   |    | <UIView: 0x8d7da00; frame = (0 0; 320 480); layer = <CALayer: 0x8d7da60>>
   |    | <UIView: 0x8d7dab0; frame = (320 0; 320 480); layer = <CALayer: 0x8d7db10>>
   |    | <UIView: 0x8d7db40; frame = (640 0; 320 480); layer = <CALayer: 0x8d7dba0>>

您可以使用多种方法来访问该滚动视图。最简单的方法是迭代self.pageViewController.view.subviews 并找到UIScrollView 的子类。由于它是唯一的子视图,因此您的循环将在一次迭代后结束。

这个解决方案是最优的吗?不,它容易出错吗?理论上,当然。有可能改变吗?不太可能,因为视图层次结构非常合乎逻辑。它至少可以为您提供快速修复,而不是因为 Apple 的小疏忽而不得不更改整个应用程序结构(为用户提供对滚动视图的访问权限)。

您应该通过https://bugreport.apple.com 向 Apple 提出功能请求。

【讨论】:

  • 嘿 Leo,我尝试循环浏览 UIPageViewController 的滚动视图,我确实可以禁用滚动,但是将滚动到顶部设置为 NO 仍然会使我的子控制器无法滚动到顶部。
  • @troop231 尝试运行这里建议的方法,看看谁在干扰滚动:stackoverflow.com/questions/8527801/…
  • 我刚刚运行了那个方法,它列出了所有的滚动视图。我怎样才能给你看?另外:在解决此问题后,我最终需要仅在我的一个内容视图控制器上禁用 pageViewController 向右平移 x 轴,我现在应该放弃 UIPageViewController 吗?
  • @Roma-MT 不,因为 Apple 不会公开他们的滚动视图。您应该提交功能增强请求,以便他们将其添加到 UIKit 的未来版本中。
  • @Mischa 已编辑。看看是不是更好。随意进一步编辑以使其更好。
【解决方案2】:

-scrollToTop 上的 iOS 7 文档说

在 iPhone 上,滚动到顶部手势无效 而不是屏幕上的一个滚动视图将 scrollsToTop 设置为 YES。

我正在以与您类似的方式使用 UIPageViewController;我的页面控制器之一是 UITableViewController。所以我被two-scrollviews-onscreen-no-scrolltotop 政策所困扰。

Kyle 通过一个不可见的按钮实现你自己的 scrollToTop 行为的想法可能是你在这种情况下最可靠的选择。

【讨论】:

  • 我实际上放弃了页面视图控制器并使用了 UIScrollView,它比页面视图控制器更好,并提供了更多选项。
【解决方案3】:

在我的情况下,UIPageViewController 的子视图控制器中有一个 UITableView。从 Leo Natan 的建议中提取,将其添加到我的子视图控制器中完成了这项工作:

- (void)viewWillAppear:(BOOL)animated 
{
    [super viewWillAppear:animated];
    self.tableView.scrollsToTop = YES; 
}

- (void)viewWillDisappear:(BOOL)animated 
{
    [super viewWillDisappear:animated];
    self.tableView.scrollsToTop = NO; 
}

如前所述,子视图控制器中相互竞争的UIScrollView 会干扰彼此的scrollsToTop 委托方法。此代码确保一次只有其中一个启用了 scrollsToTop。

【讨论】:

    【解决方案4】:

    我用过这个:

    for v in view.subviews {
        if v.isKindOfClass(UIScrollView) {
            (v as! UIScrollView).scrollsToTop = false
        }
    }
    

    但如果还有其他可以滚动到顶部的滚动视图,您的将无法使用。使用此脚本找出您的其他滚动视图: https://stackoverflow.com/a/33443757/2589276 ,然后禁用滚动到顶部。

    然后在要滚动到顶部工作的视图控制器中,添加以下内容:

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        tableView.scrollsToTop = true
    }
    
    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        tableView.scrollsToTop = false
    }
    

    【讨论】:

      【解决方案5】:

      我认为最好的解决方案是使用 UIPageViewControllerDelegate 协议的以下两种方法(快速和肮脏的版本,考虑到屏幕上只有一个视图控制器):

      - (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers
      {
          ((UITableViewController *)pendingViewControllers[0].tableView).scrollsToTop = YES;
      }
      
      - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
      {
          ((UITableViewController *)previousViewControllers[0].tableView).scrollsToTop = NO;
      }
      

      编辑:最好使用已接受答案的approach outlined in the last paragraph

      【讨论】:

      • 有点,是的,带有免责声明“这个解决方案是最优的吗?不。它容易出错吗?理论上,当然。”所以我想我会分享另一种方法。没关系
      • 这应该是公认的答案。这是最干净的解决方案,一点也不脆弱或骇人听闻。请注意,您需要将这些行分成两行 - 在访问它的 tableView 之前将其分配给一个表视图控制器,否则它仍然是没有 tableView 属性的 id。此外,您可能希望在分配它之前进行一些自省。另请注意,这也适用于 UICollectionViewController - 将 tableView 更改为 collectionView。太棒了,谢谢!
      • 如果您查看其他答案中的聊天内容,您会看到我们终于找到了它并修复了它。这是一种危险的方式,并没有涵盖从一个视图控制器到另一个视图控制器的所有可能转换。最好在视图控制器本身中进行,viewWillAppearviewWillDisappear
      • @LeoNatan 是对的,his accepted approach(在 viewWillAppear 上设置为 YES,在 viewWillDisappear 上设置为 NO)更好。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-13
      • 1970-01-01
      • 1970-01-01
      • 2012-02-15
      • 2012-05-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多