【问题标题】:Panning objects on a UIScrollView在 UIScrollView 上平移对象
【发布时间】:2014-02-26 12:43:57
【问题描述】:

我正在开发一个应用程序,其中有几个 UIView 对象,它们是 UIScrollView 对象的子视图。我以编程方式创建子视图,并根据关联对象的属性将它们放置在滚动视图上。允许用户在滚动视图上移动这些子视图。通常这是可行的,但有时滚动视图会抓取平移手势。

如果触摸位置在其中一个子视图内,我想做的是抑制滚动视图手势识别器。

我可以通过查看滚动视图的手势识别器数组并查找UIScrollViewPanGestureRecognizer 对象来找到滚动视图手势识别器。我假设只有一个。

我的一个想法是让我的视图控制器成为这个手势识别器的代表,然后如果触摸在其中一个子视图的范围内,让代表抑制它。

这是处理这种情况的最佳方法,还是有更好的方法?

我已经做了类似的事情,在我对我自己的问题的回答中有所描述。 How to get stepper and longpress to coexist?

嗯。看起来识别滚动视图的UIScrollViewPanGestureRecognizer 比我预期的要困难。任何有关这样做的提示将不胜感激。

我的想法行不通。为了编写我的想法,我必须让我的 VC 成为滚动视图的平移手势识别器的代表。但是,当我这样做时,我得到了这个错误:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UIScrollView's built-in pan gesture recognizer must have its scroll view as its delegate.'

这是我使用的代码。在viewDidLoad 中,我调用了一个获取滚动视图的平移手势识别器并将self 设置为委托的方法(self.scrollViewPanGestureRecognizer 只是一个存储它的属性):

self.scrollViewPanGestureRecognizer = [self.scrollView panGestureRecognizer];
self.scrollViewPanGestureRecognizer.delegate = self;

然后我实现了这个委托方法:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
   //Disable touch if touch location is in a subview.
   BOOL enableGestureRecognizer = YES;

   if (gestureRecognizer == self.scrollViewPanGestureRecognizer) {
      CGPoint touchLocation = [touch locationInView:self.scrollView];
      for (UIView *s in self.scrollView.subviews) {
         if (CGRectContainsPoint(s.frame, touchLocation)) {
            enableGestureRecognizer = NO;
         }
      }
   }
   return enableGestureRecognizer;
}

似乎是个好主意,但看起来我不能让我的 VC 成为代表。

刚刚尝试在滚动视图上将scrollEnabled 设置为NO。这成功地禁用了滚动,但它并没有解决问题。视图仍然偶尔不响应手势。考虑到可能是某些错误导致手势识别器从对象上脱落,我要求调试器显示有问题的视图的手势识别器。他们还在那里。我被难住了。

更新:新信息。我终于意识到没有响应的子视图是屏幕右侧的子视图。经过仔细测试,这似乎只发生在横向,并且仅当手指位置在纵向右边缘的右侧时,即320点。显然,当旋转到横向时,某些东西没有被处理。一切看似正常,但手势无法识别。

只是为了笑,我决定在方法viewDidLayoutSubviews 中显示框架和边界以及内容区域。我得到的是:

self.view.frame             is {{0, 0}, {480, 320}} 
self.view.bounds            is {{0, 0}, {480, 320}} 
self.scrollView.frame       is {{0, 0}, {480, 320}} 
self.scrollView.bounds      is {{0, 0}, {480, 320}}
self.scrollView.contentSize is {480, 320}

我似乎错过了什么。旋转时还需要设置什么?

【问题讨论】:

    标签: ios uiscrollview uigesturerecognizer


    【解决方案1】:

    使用requireGestureRecognizerToFail: 方法。

    您希望滚动视图平移手势 (scrollViewGesture) 在其中一种手势发生在其子视图上时失败。
    因此,当您向子视图 (subViewGesture) 添加平移手势时,请将以下属性设置为

    scrollViewGesture.requireGestureRecognizerToFail =subViewGesture;
    

    【讨论】:

    • 初步测试似乎证实了这一点。在接受这个答案之前我需要做更多的测试,因为这个问题只是一个偶然的问题。
    • 此方法的一个潜在问题是它可能会在滚动视图的平移手势识别器的性能中引入延迟。不过,到目前为止,我没有看到明显的延迟。一个示例延迟是点击需要双击才能失败。我相信这会导致默认的 0.2 秒延迟。显然,我看到的失败几乎是立即返回。
    • 经过进一步测试,好像这样不行。
    • 我怀疑这不起作用的原因是该方法设置了两个识别器之间的关系。为了让它工作,滚动视图的识别器需要要求所有其他的都失败。为了让它工作,我可能需要将识别器附加到滚动视图而不是子视图并使其失败,除非触摸位置位于其中一个子视图内。现在这看起来和其他提供的解决方案一样复杂。
    • 可以用多个其他手势requireGestureRecognizerToFail 吗?还是只有一个?
    【解决方案2】:

    一种解决方案:与其玩弄手势识别器,不如将它们全部禁用并直接使用touchesBegantouchesMovetouchesEnded。这可能需要一些工作,但很确定它会完全按照您想要的方式工作。

    需要在subviews上禁用用户交互,在scrollview上禁用滚动,直接修改scrollview的contentOffset

    【讨论】:

    • 我想我会选择另一个答案,因此我不必完成与此选项相关的所有工作。到目前为止一切顺利。
    【解决方案3】:

    我将此答案设为社区 wiki,因为我尚未完全制定出此解决方案。主要是利用文档中的这一点:

    子类可以覆盖 touchesShouldBegin:withEvent:inContentView:pagingEnabledtouchesShouldCancelInContentView: 方法(由 滚动视图)来影响滚动视图如何处理滚动 手势。

    【讨论】:

      【解决方案4】:

      我找到了解决方案。我忘记了子视图没有直接放入滚动视图中。有一个视图最初占据了子视图所在的滚动视图的边界。层次结构是这样的:

      self.view
          scroll view
              UIView (fills whole scroll view)
                  subview1
                  subview2
                  subviewn
      

      在我处理旋转的代码中,我没有调整放置子视图的 UIView 的大小。纠正这个问题解决了这个问题。

      我最初尝试将没有 UIView 超级视图的子视图放在它们和滚动视图之间,但由于某种原因它不起作用。添加这个额外的层解决了这个问题,但我忘了在旋转时处理调整大小。

      所以我猜手势识别器没有响应,因为虽然它们是可见的,但它们超出了其父视图的范围。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-10-05
        • 2016-09-07
        • 2013-08-22
        • 1970-01-01
        • 2014-07-27
        • 1970-01-01
        • 2015-11-10
        相关资源
        最近更新 更多