嵌套的UIScrollViews和事件路由

13

我有两个滚动视图,两者都应该垂直滚动。外部滚动视图(红色)包含一个搜索栏和内部滚动视图(蓝色)。内部滚动视图应该无限滚动(它包含图像/项并具有无限滚动实现)。

我希望此控制器的工作方式如下:

向下滚动时,外部滚动视图应首先滚动,搜索栏应消失(滚动出内容区域)。仅在此之后,内部滚动视图才开始滚动。 当向上滚动时,内部滚动视图应完全滚动到其顶部。然后,外部滚动视图才会接收滚动事件,并最终向上滚动以再次使搜索栏可见。

如果只是将它们嵌套在界面构建中而没有进行任何修改,则内部滚动视图会捕获所有滚动事件,反过来也是如此。

请注意,我在这里使用内部滚动视图作为简化隐喻。在我的应用程序中,实际上我有一个控件,它具有一个带有嵌套表格视图的滚动视图(滚动视图让我横向分页,表格视图让我垂直滚动)。

enter image description here


你有没有找到解决方案或变通方法? - Just a coder
6个回答

2
如果您能够实现,为2个滚动视图设置一个共同的UIScrollViewDelegate,并实现以下内容:
- (void) scrollViewDidScroll: (UIScrollView*) scrollView
{
  if (scrollView == self.mainScrollView)
  {
    /* Handle instances when the main scroll view is already at the bottom */
    if (   scrollView.contentOffset.y
        == scrollView.contentSize.height - scrollView.bounds.size.height)
    {
      /* Stop scrolling the main scroll view and start scrolling the
       * inner scroll view
       */
      self.innerScrollView.scrollEnabled = YES;
      self.mainScrollView.scrollEnabled = NO;
    }
    else
    {
      /* Start scrolling the main scroll view and stop scrolling the
       * inner scroll view
       */
      self.innerScrollView.scrollEnabled = NO;
      self.mainScrollView.scrollEnabled = YES;
    }
  }
  else if (scrollView == self.innerScrollView)
  {
    /* Handle instances when the inner scroll view is already at the top */
    if (self.innerScrollView.contentOffset.y == 0)
    {
      /* Stop scrolling the inner scroll view and start scrolling the
       * main scroll view
       */
      self.innerScrollView.scrollEnabled = NO;
      self.mainScrollView.scrollEnabled = YES;
    }
    else
    {
      /* Start scrolling the inner scroll view and stop scrolling the
       * main scroll view
       */
      self.innerScrollView.scrollEnabled = YES;
      self.mainScrollView.scrollEnabled = NO;
    }
  }
}

请注意,我没有测试过这个方法,但其逻辑可能是这样的(要么启用滚动,要么禁用用户交互,或者其他方式)。很可能这并不足以解决您的问题,但我相信一个常见的UIScrollViewDelegate可以解决您的问题。

我已经尝试过这个方法,它“有点”有效。问题在于,如果按照你提出的方式进行操作,滚动/滑动手势无法从一个滚动视图传递到另一个滚动视图。你必须再次拖动/滑动才能滚动到第二个滚动视图。 - Sebastian
你可以使用scrollView的panGestureRecognizer获取用户平移时的速度,计算动画持续时间和内容偏移量差,并在另一个scrollView到达顶部/底部时,在mainScrollView或innerScrollView上模拟一个平移。 - Kenn Cal
我找到的解决@Sebastian遇到的问题的方法是将Replace self.mainScrollView.scrollEnabled = NO; to self.mainScrollView.scrollEnabled = YES 我们不必为mainScrollView禁用滚动,因为mainScrollView的焦点会根据innerScrollView的滚动位置和滚动方向自动处理 试一下,它对我有效。 - Dinesh Balu

1
我给出了一个scrollview的例子,你需要创建另一个scrollview,并根据动态高度和内容大小进行添加,它将起作用。
// .h文件
@property (nonatomic, strong) UIScrollView *scrlSearch;

// .m文件 // 视图加载完成
scrlSearch = [[UIScrollView alloc] init];

// For first scroll screen height was ((total screen height / 100 )* 10% )
// For Second scroll screen height was ((total screen height / 100 )* 90% )
scrlSearch.frame = CGRectMake(0, 0, (YourScreenWidth), (YourScreenHeight));

// YourVIEW = add any view to scrollbar
[scrlSearch addSubview:YourVIEW];

CGSize contentSize = scrlSearch.frame.size;
// YourContentHeight = dynamic content or static content height
contentSize.height = YourContentHeight;

// set the ContentHeight for scrolling
[scrlSearch setContentSize:contentSize];
// add the scrollview into delegate
[scrlSearch setDelegate:self];

6
抱歉,但这怎么算得上是一个回答呢? - followben
@followben 我用UIscrollview做了一些示例,它对我很有效,所以我分享了这段代码。 - Karthik

0

只使用一个scrollView,并为搜索栏设置contentInset/contentOffset。 大致如下:

  UIEdgeInsets oldEdgeInset = [[self scrollView] contentInset];
  CGRect f = [[self searchBar] frame];
  UIEdgeInsets newEdgeInset = UIEdgeInsetsMake(CGRectGetMaxY(f), 0, 0, 0);
  CGPoint offset = [[self scrollView] contentOffset];
  offset.y += oldEdgeInset.top - newEdgeInset.top;
  [[self scrollView] setContentOffset:offset];
  [[self scrollView] setContentInset:newEdgeInset];
  [[self searchBar] setFrame:f];

很不幸,我的情况比这个要复杂一些 :( 内部滚动视图实际上是一个包含多个表视图的水平分页器。它是一个自包含的视图,我不能轻易地扩展它。我正在寻找更多关于覆盖父视图的解决方案,这将让我将事件路由到适当的滚动视图... - Sebastian

0

你应该在外部滚动视图上添加内部滚动视图,但是内部滚动视图的 'y' 位置应该是外部滚动视图的内容偏移量,就像...

innerScrollView.frame = CGRectMake(10, outerScrollView.contentSize.height, 300, 440);

在此之后,您可以在此innerScrollView上添加任何视图,您可以为innerScrollView设置contentOffset等。

最后,您应该增加外部滚动视图的contentSize。

outerScrollView.contentSize = CGSizeMake(320, innerScrollView.frame.size.height+actualContentSizeOfOuterScroolView);

我认为这对你有帮助!


0

我不确定你现有的结构和想要实现的结构是否一致。

我已经制作了一个测试项目,可以在这里找到。

但是该项目肯定会帮助您同时管理不同的滚动视图。

虽然该应用程序可能不完美,但它将为您提供实现解决方案的一些想法。

希望能对您有所帮助。

祝好!


0
在您的最顶层视图(在所有滚动视图之上,在您的视图控制器的视图上)创建一个轻扫手势识别器,并使其识别UISwipeGestureRecognizerDirectionUp
然后,您需要在外部滚动视图滚动时通知您的控制器。 一旦它向下滚动,就添加手势识别器。当它再次到达顶部(outerScrollView.contentOffset ==(0,0))时,删除手势识别器。
手势应该在存在时“吃掉”所有滑动事件,使您的内部滚动视图不接收触摸事件,因此不会滚动。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接