嵌套的 UIScrollViews 和事件路由

Posted

技术标签:

【中文标题】嵌套的 UIScrollViews 和事件路由【英文标题】:Nested UIScrollViews and event routing 【发布时间】:2013-04-19 11:24:04 【问题描述】:

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

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

当我向下滚动时,外部滚动视图应该首先滚动并且搜索栏应该消失(滚动出内容区域)。只有在那之后,内部滚动视图才应该开始滚动。 向上滚动时,内部滚动视图应一直滚动到其顶部。只有这样,外部滚动视图才应该接受滚动事件并最终向上滚动以使搜索栏再次可见。

如果我只是将它们嵌套在 IB 中而不进行任何修改,则内部滚动视图会捕获所有滚动事件,并且它会以相反的方式工作。

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

【问题讨论】:

您有没有找到解决方案或变通方法? 【参考方案1】:

如果可以的话,在 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 是您问题的解决方案。

【讨论】:

我已经尝试过了,它“有点”有效。问题是,如果您按照您的建议进行操作,滚动/滑动手势不会从一个滚动视图转移到另一个滚动视图。您必须再次拖动/滑动到第二个滚动视图滚动。 您可以使用 scrollView 的 panGestureRecognizer 来获取用户平移的速度,计算动画持续时间和内容偏移量增量,并在 mainScrollView 或 innerScrollView 上伪造一个平移已到达顶部/底部。 我为@Sebastian 遇到的问题找到的解决方案是 替换 self.mainScrollView.scrollEnabled = NO; to self.mainScrollView.scrollEnabled = YES 我们不必禁用 mainScrollView 的滚动,因为 mainScrollView 的焦点是自动处理的,取决于 innerScrollView 滚动位置和滚动方向 试试看,它适用于我跨度> 【参考方案2】:

我给出了一个滚动视图的示例,你必须创建一个滚动视图并根据动态高度和内容大小添加它才能工作。

// .h 文件

@property (nonatomic, strong) UIScrollView *scrlSearch;

// .m 文件 // ViewDidLoad

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];

【讨论】:

对不起,这怎么可能是一个答案? @followben 我使用 UIscrollview 做了一些示例。它对我有用,这就是我分享这段代码的原因。【参考方案3】:

只使用一个 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];

【讨论】:

不幸的是,在我的情况下它比这更复杂一点:( 内部滚动视图实际上是一个包含多个表视图的水平寻呼机。它是一个独立的视图,我无法轻松扩展。我我正在寻找一个更倾向于覆盖超级视图的解决方案,它可以让我将事件路由到适当的滚动视图......【参考方案4】:

我不确定您拥有并想要实施的结构..

我做了一个测试项目 find here

但该项目肯定会帮助您一起管理不同的滚动视图..

该应用程序可能并不完美,但会给您一些实现解决方案的想法。

希望对你有帮助..

干杯

【讨论】:

【参考方案5】:

您应该在外部滚动视图上添加内部滚动视图,但内部滚动视图的 'y' 位置应该是外部滚动视图集之外的内容...

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

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

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

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

我觉得对你有帮助!

【讨论】:

【参考方案6】:

为您的最顶层视图(在所有滚动视图之上,在您的视图控制器的视图上)创建一个滑动手势识别器,并使其识别UISwipeGestureRecognizerDirectionUp

然后,您需要在外部滚动视图滚动时通知您的控制器。 向下滚动后,添加手势识别器。当它再次到达顶部时(outerScrollView.contentOffset == (0,0)),移除手势识别器。

手势在出现时应该“吃掉”所有滑动事件,使您的内部滚动视图不会接收到触摸事件,因此不会滚动

【讨论】:

以上是关于嵌套的 UIScrollViews 和事件路由的主要内容,如果未能解决你的问题,请参考以下文章

滚动发生时嵌套 UIScrollViews 并获取信息

iOS嵌套的UIScrollViews没有响应

处理嵌套 UIScrollViews 中的冲突手势

iPhone - 嵌套 UIScrollViews 用于水平分页和垂直滚动

嵌套 UIScrollViews 中的滚动问题

具有水平滚动的嵌套 UIScrollViews