iOS:我能否覆盖UIScrollView的捏合缩放行为?

5
我正在一个UIView上绘制图表,该视图包含在一个UIScrollView中,因此用户可以水平滚动以查看整个图表。
现在,当用户用两个手指捏合时,我想缩放图表,但是我想只更改X比例而不更改Y比例,以X方向进行缩放。
我认为我必须捕捉捏合手势并重新绘制图表,覆盖默认的缩放行为。
但是有没有办法做到这一点?
我一直很难在UIScrollView上捕捉捏合手势,因为它开始滚动时会取消触摸。我希望即使在UIScrollView取消触摸后也能正常工作。:(
谢谢, Kura
4个回答

12

虽然您不能删除现有的捏合手势识别器,但可以禁用它,然后添加自己的手势识别器:

// Disable existing recognizer
for (UIGestureRecognizer* recognizer in [_scrollView gestureRecognizers]) {
    if ([recognizer isKindOfClass:[UIPinchGestureRecognizer class]]) {
        [recognizer setEnabled:NO];
    }
}

// Add our own
UIPinchGestureRecognizer* pinchRecognizer = 
  [[UIPinchGestureRecognizer alloc] initWithTarget:self 
                                            action:@selector(pinch:)];
[_scrollView addGestureRecognizer:pinchRecognizer];
[pinchRecognizer release];

接着,在

- (void) pinch:(UIPinchGestureRecognizer*)recognizer { .. }
[recognizer locationOfTouch:0 inView:..]
[recognizer locationOfTouch:1 inView:..]

需要确定用户是水平捏合还是垂直捏合。


这是一个简单的解决方案。 :) 感谢 edsko! - Taka
即使没有禁用退出手势,您也可以添加新手势。 - Naveen Shan
2
来自遥远未来的快速评论:在这篇答案写出三个月后,iOS 5添加了-[UIScrollView pinchGestureRecognizer],因此您不必进行线性搜索并希望Apple没有做任何太过非正统的事情。 - Tommy

5
您应该访问在UIView中定义的手势识别器gestureRecognizers,滚动视图中使用了多个手势识别器,找出其中的缩放手势识别器并调用removeGestureRecognizer:从滚动视图中移除它,然后创建您自己的手势识别器并让它完成工作,最后使用addGestureRecognizer:将其添加回去。
这些都是公共API,手势识别器及其顺序目前不是公开的,因此在访问它们时需要进行防御性编程。
(这是一种完全有效的操作UIKit视图的方法,苹果不会/不应该对此有任何问题 - 尽管他们不能保证它在未来的版本中能正常工作)

+1 我正在为此编写答案,并提供代码示例,但需要做的是手势识别器在触摸事件之前触发,通常不会发送touchesBegan和类似的事件。循环遍历gestureRecognizers并查找任何一个isKindOfClass:[UIPinchGestureRecognizer class]。 - Joe
bshirley和Joe,谢谢你们的回复。我在UIScrollView实例上找到了3个gestureRecognizers,其中一个是UIScrollViewPinchGestureRecognizer。我试图将其删除并添加我的自定义手势识别器,但它会崩溃,因为UIScrollView始终尝试专门向已删除和释放的UIScrollViewPinchGestureRecognizer对象发送消息。我想到了一个主意,调用[UIGestureRecognizer removeTarget:action:]来删除默认的捏合操作并添加新的操作,但该操作似乎是[UIScrollView handleGesture:],这是没有记录的。:( - Taka
添加你的识别器,掌握该识别器,然后告诉该识别器它需要在成功之前失败。 - bshirley
谢谢 bshirley。我会尝试的。;) - Taka

1
你应该能够子类化UIScrollView并覆盖touchesBegan:方法。不要调用[super touchesBegan:],而是根据需要调整缩放:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    //Anything you want. Probably you would want to store all the touches
    //or their values, so that you can compare them to the touches
    //in the touchesEnded: method, 
    //thus letting you know what the pinch amount was
}

如果你愿意,你可以判断它是否是捏合手势,如果不是,则调用super方法,只有自定义捏合手势时才处理它。


这可能不再起作用,因为UIScrollView现在使用多个手势识别器,在这些手势识别器和touch方法之间的交互中,通常方法会失败。当尝试从滚动视图中实现“拖动”时,我必须使用手势识别器来完成它。 - bshirley
我想这是有道理的。虽然如果你仍然想以这种方式做,你应该能够只需子类化UIView并获取其触摸事件,然后使用它们来操作放在UIView上方的UIScrollView? - Matthew Horst
Matt,谢谢你的回复。我已经尝试过了,但是一旦滚动开始,UIScrollView就会取消触摸,并且在此之后无法再处理它们。如果我子类化UIScrollView,或者子类化UIView并将其放在UIScrollView上,情况也是一样的。 - Taka

0
Edsko和bshirley的回答很好,但他们没有告诉在哪里放置代码。
首先,我把它放在viewDidLoad方法中,但在滚动视图中找不到Pinch Gesture Recognizer(可能是因为我的滚动视图是IBOutlet)。
然后我尝试在viewWillAppear或viewDidAppear中,发现了UIPinchGestureRecognizer。

是的,你说得对。还有viewDidLayoutSubviews - Martin

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