【问题标题】:Cross Directional UIScrollViews - Can I Modify the Scrolling Behaviour?交叉方向 UIScrollViews - 我可以修改滚动行为吗?
【发布时间】:2013-09-30 10:13:41
【问题描述】:

滚动视图的工作原理如下: 一个滚动视图在水平方向启用分页。此滚动视图的每个“页面”都包含一个垂直滚动的 UITableView。如果不进行修改,这可以正常工作,但并不完美。

不正确的行为:当用户在表格视图上上下滚动,但又想快速翻到下一页时,水平滑动/滑动最初不起作用 -在表格视图静止之前它不会起作用(即使滑动非常明显是水平的)。

它应该如何工作:如果滑动明显是水平的,我希望页面改变,即使表格视图仍在滚动/弹跳,因为这也是用户所期望的.


我怎样才能改变这种行为 - 最简单或最好的方法是什么?


注意 由于各种原因,某些答案中所述的 UIPageViewController 将不起作用。我如何使用交叉方向 UIScrollViews 来做到这一点(/one 是一个表格视图,但你明白了)?我已经用头撞墙了好几个小时了——如果你认为你能做到这一点,那么我会很乐意奖励赏金。

【问题讨论】:

  • 您的最低 iOS 目标是多少?如果 iOS 6.0+,不使用UIScrollView,您是否考虑过在此处使用UIPageViewController
  • (从技术上讲,您可以在 iOS 5.0+ 中使用 UIPageViewController,但我相信 UIPageViewControllerTransitionStyleScroll 仅适用于 iOS 6.0+)
  • @JRG-Developer 我最初确实考虑过 UIPageViewController,但有几个原因它对我不起作用。我需要访问滚动视图的“scrollViewDidScroll:”方法,该方法不适用于页面视图。
  • 你想通过scrollViewDidScroll:方法完成什么?也就是说UIPageViewControllerDelegate上有类似的委托方法,比如pageViewController:willTransitionToViewControllers:pageViewController:didFinishAnimating:transitionCompleted:,可以让你知道发生了水平滚动事件。
  • @JRG-开发类似水平视差的视图效果 - UIPageViewController 委托中的方法在滚动发生时不提供跟踪信息,仅在之后提供。

标签: ios objective-c uitableview uiscrollview


【解决方案1】:

根据我对问题的理解,只有在 tableView 滚动时,我们才想更改默认行为。所有其他行为都将相同。

子类UITableViewUITableViews 是 UIScrollViews 的子类。在UITableView 子类上实现一个UIScrollViewUIGestureRecognizer 的委托方法

- (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer *)otherGestureRecognizer
{
    //Edit 1
    //return self.isDecelerating;
    //return self.isDecelerating | self.bounces; //If we want to simultaneous gesture on bounce and scrolling
    //Edit 2
    return self.isDecelerating || self.contentOffset.y < 0 || self.contentOffset.y > MAX(0, self.contentSize.height - self.bounds.size.height); // @Jordan edited - we don't need to always enable simultaneous gesture for bounce enabled tableViews
}

因为我们只想在 tableView 减速时更改默认手势行为。

现在将所有 'UITableView 的类更改为您新创建的 tableViewSubClass 并运行项目,当 tableView 滚动时滑动应该可以工作。 :]

但是当 tableView 滚动时,滑动看起来有点太敏感了。让我们对滑动进行一些限制。

子类UIScrollView。在UIScrollView 子类上实现另一个UIGestureRecognizer 的委托方法gestureRecognizerShouldBegin:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer 
{
    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
        CGPoint velocity = [(UIPanGestureRecognizer *)gestureRecognizer velocityInView:self];
        if (abs(velocity.y) * 2 < abs(velocity.x)) {
            return YES;
        }
    }
    return NO;
}

我们想让“滑动清晰地水平”。上面的代码仅在 x 轴上的手势速度是 y 轴上的两倍时才允许手势开始。 [如果您愿意,请随意增加硬编码值“2”。值越高,滑动需要越水平。]

现在将“UiScrollView”类(具有多个 TableView)更改为您的 ScrollViewSubClass。运行项目。 :]

我在 gitHub 上做了一个项目https://github.com/rishi420/SwipeWhileScroll

【讨论】:

  • 谢谢!只是一个小问题——当表格视图“反弹”时,isDecelerating 方法似乎设置为 no。对于内容高度小于滚动视图高度的表格,这是一个问题 - 有没有办法解决这个问题?
  • 我认为弹跳只是告诉你滚动视图是否可以弹跳,而不是实际上它当前是否正在弹跳。我去了:self.isDecelerating || self.contentOffset.y MAX(0, self.contentSize.height - self.bounds.size.height);
  • 这个解决方案效果很好,谢谢!我会多玩一点,看看是否可以让表格视图在左右翻页时保持滚动,而不是在用户这样做时突然停止。
  • 谢谢。我认为我们需要始终为启用弹跳的 tableViews 启用同步手势。使用您的解决方案编辑的答案,Git 也更新了。当您找到“突然停止”的解决方案时,请随时在 git 存储库中进行贡献。
  • 玩了一段时间后,它在模拟器上运行良好,但在实际的 iOS 设备上表现有点奇怪。为了解决这个问题,我摆脱了解决方案的第二部分 - 似乎如果用户的平移不够快,速度将为零,因此平移将不起作用。从功能上讲,它并不是我想象的那样工作(因为两个视图可以同时滚动),但仍然有效!
【解决方案2】:

虽然苹果不太喜欢这种方法:

重要提示:您不应在 UIScrollView 对象中嵌入 UIWebView 或 UITableView 对象。如果这样做,可能会导致意外行为 因为两个对象的触摸事件可能会混淆和错误 处理。

我找到了一个很好的方法来完成这个。

这是该问题的完整解决方案。要在 UITableView 滚动时滚动 UIScrollView,您需要禁用它的交互。

- (void)viewDidLoad
{
    [super viewDidLoad];
    _myScrollView.contentSize = CGSizeMake(2000, 0);
    data = [[NSMutableArray alloc]init];
    for(int i=0;i<30;i++)
    {
        [data addObject:[NSString stringWithFormat:@"%d",i]];
    }

    UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
    [self.view addGestureRecognizer:tap];
}

- (void)handleTap:(UITapGestureRecognizer *)recognizer
{
    [_myTableView setContentOffset:_myTableView.contentOffset animated:NO];
}

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
          scrollView.userInteractionEnabled = NO;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
          scrollView.userInteractionEnabled = YES;
}

总结上面的代码,如果UITableView 正在滚动,请将userInteractionEnabled 设置为NO,这样UIScrollView 就会检测到滑动。如果UITableView 正在滚动并且用户点击屏幕,userInteractionEnabled 将设置为 YES。

【讨论】:

  • 虽然您的解决方案可能有效,但它可能会在未来的版本中中断。使用文档总是一个好主意;)
  • @HAS 如果 OP 问题最初是针对文档的,我无法使用文档。苹果引用引用了用户问题。
  • 对此解决方案 +1。文档确实对此提出了警告,但自 iOS 4.x(现已更新到 iOS 7)以来,我在 App Store 中有一个应用程序,它在 UIScrollView 内有 UITableView 对象,并且我想要的行为运行良好,Apple 没有问题批准它。如果将来的某些 iOS 版本破坏了它,我会处理它。在此之前,发布您的应用程序并继续解决其他问题。
  • 这和我想要的交互如此接近,但只有一个问题。如果用户想要连续滚动表格两次,那么在第一次滚动完成减速之前,表格的第二次滚动将不起作用。有没有办法根据平移方向选择性地选择是否启用用户交互?
  • 这种方法的主要问题是用户不能在不等到表格视图停止移动的情况下连续垂直滚动两次——我还没有找到解决方法。除非发生变化,否则我将不得不使用发布的解决方案(使用 git hub 项目)
【解决方案3】:

不要使用UIScrollView 作为这些多个表视图的容器,而是尝试使用UIPageViewController

您甚至可以将其作为child view controller 集成到您现有的视图控制器设置中(直接替换UIScrollView)。

此外,您可能希望实现来自UIPageViewControllerDataSource 的所需方法,以及可能来自UIPageViewControllerDelegate 的一种或多种方法。

【讨论】:

  • 我需要访问 scrollViewDidScroll:这就是我使用 UIScrollView 而不是 UIPageView 的原因。所以还是卡在了同一个地方。好主意 - 谢谢。
  • 您应该能够通过它的 gestureRecognizers 属性访问 UIPageViewController 的平移手势识别器,它应该提供与 scrollViewDidScroll 相同的信息:(从我的手机写,所以不是 100% 肯定,但很肯定)
【解决方案4】:

您是否尝试过您的表格和滚动条的directionalLockEnabled 方法,并将它们设置为一个水平,另一个垂直?

编辑:

1) 你想要做的事情非常复杂,因为触摸会等待一段时间(比如 0.1 秒)才能知道你的动作是什么。如果您的桌子在移动,无论它是什么,它都会立即触动您(因为它被认为是对它的反应性移动)。

除了从头开始覆盖触摸移动以立即检测您想要的移动类型(例如移动是否是水平的)之外,我没有看到任何其他解决方案,但要做到这一点非常困难。

2) 我可以建议您的另一个解决方案是让您的表格具有左右边距,您可以在其中触摸父滚动(页面如此),然后即使您的表格正在滚动,如果您触摸此处,只会触摸您的分页滚动.它更简单,但可能不适合您的设计......

【讨论】:

  • 是的。您实际上并没有将 directionalLockEnabled 设置为特定方向,它会在您开始滚动后确定方向并将其锁定到该方向。
【解决方案5】:

使用UIPageViewController 并在-viewDidLoad 方法(或任何其他最适合您的需要或设计的方法)中获取UIPageViewControllerUIScrollView 子视图并为其分配一个委托。请记住,它的委托属性不会为零。因此,您可以选择将其分配给另一个引用,然后将符合UIScrollViewDelegate 的对象分配给它。例如:

id<UIScrollViewDelegate> originalPageScrollViewDelegate = ((UIScrollView *)[pageViewController.view.subviews objectAtIndex:0]).delegate;
[((UIScrollView *)[pageViewController.view.subviews objectAtIndex:0]) setDelegate:self];

这样您就可以轻松实现UIScrollViewDelegate 方法。而您的 UIPageViewController 将调用您代表的 -scrollViewDidScroll: 方法。

顺便说一句,您可能不得不保留原始委托,并使用该对象响应委托方法。你可以在我的 UI 控件项目hereViewPagerController 类中看到一个示例实现

【讨论】:

  • 谢谢!事实证明 UIPageViewController 有一些其他奇怪的东西不会让我克服它们......不是小问题,所以 UIPageViewController 在我的情况下不起作用。
【解决方案6】:

我最近遇到了同样的事情。我的UIScrollview 处于分页模式,每个页面都包含一个UITableView,就像您描述的那样它有效,但不像您预期​​的那样有效。就这样解决了。

首先我禁用了UIScrollview的滚动

然后我在实际的UITableView 中添加了一个UISwipeGestureRecognizer,用于左右滑动。

这些滑动的动作是:

[scroll setContentOffset:CGPointMake(currentPointX + 320, PointY) animated:YES];
//Or
[scroll setContentOffset:CGPointMake(currentPointX - 320 , PointY) animated:YES];

这完美无缺,唯一的缺点是如果用户在UITableVIew 上拖动手指,这将被视为滑动。他将无法在同一屏幕上看到屏幕 A 的一半和屏幕 B 的一半。

【讨论】:

  • 嗯,有趣!不幸的是,我实际上需要能够一次看到每个屏幕的一半。不过我会尝试一下这个想法,看看有没有什么结果。
  • @Jordan 我没试过,但你可以使用触摸事件来拖动UITableView,原理与上述相同。听起来是个很酷的测试功能。
  • 使用触摸事件来控制表格视图偏移的唯一问题是 UITableView 的惯性 - 这不是一项简单的任务,而且很难获得复制 Apple 滚动视图感觉的东西。
  • @Jordan 惯性可以通过UIScrollView页面滚动来控制。滑动 -> 去偏移。拖动 -> 拖动对象。 release-> 检查 x 是否大于当前页面/2 - 如果是,则转到页面 +1 ,否则返回当前页面。所有这些都将被动画化,并给人一种滚动视图的感觉。
【解决方案7】:

您可以将滚动视图和表格视图子类化,并将此手势识别器委托方法添加到它们中的每一个...

 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer    
     shouldRecognizeSimultaneouslyWithGestureRecognizer:
                     (UIGestureRecognizer *)otherGestureRecognizer {
        return YES;
   }

我不能确定这正是你所追求的,但它可能会接近。

【讨论】:

  • 啊,已经尝试过了,但这不会导致滚动被定向锁定抱歉。垂直平底锅应该用于表格视图,水平平底锅适用于滚动视图 - 而不是同时用于两者。
猜你喜欢
  • 1970-01-01
  • 2011-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-17
  • 1970-01-01
  • 2016-06-19
  • 1970-01-01
相关资源
最近更新 更多