从 UIScrollview 向前触摸和手势到视图

Posted

技术标签:

【中文标题】从 UIScrollview 向前触摸和手势到视图【英文标题】:Forward Touches and Gestures to Views from UIScrollview 【发布时间】:2013-11-01 08:57:42 【问题描述】:

我在转发手势和触摸时遇到了一些问题。我玩了很多次,但我无法让它按照我想要的方式工作。

基本上我想用两根手指控制双屏上的滚动视图,并将其他所有内容转发到覆盖滚动视图后面的 ipad 视图。

为了能够控制双屏上的滚动视图,我将UIScrollView 子类化并将其添加为具有清晰背景的覆盖视图到 ipad 屏幕。

然后我将它与一个委托连接起来,以将其拖动和内容转发到双屏上的滚动视图。这完美地工作。

正如我所写,我希望滚动视图只响应 2 个手指滚动,所以我将其设置为

ScrollView.panGestureRecognizer.minimumNumberOfTouches = 2;

但是滚动视图会吃掉所有的触摸,除了 2 根手指触摸到后面的视图之外,我没有正确转发其他所有内容。 我认为覆盖

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

必须做到这一点,但我无法正确检测屏幕上的手指数量。

【问题讨论】:

【参考方案1】:

我想分享/解释问题的解决方案。也就是说,我还想指出,hatfinch 的输入将我引向了正确的方向。非常非常感谢你!

尝试将视图/滚动视图作为覆盖顶层时的问题是顶层不知道它的“下一个响应者”。将视图/滚动视图作为底层视图将解决此问题。 您可能需要微调该底层滚动视图中任何滚动视图的触摸行为以使行为正确(例如设置最大触摸次数)

解决办法是继承一个UIScrollview,重写这个方法 override the touchesBegan: and other touch methods as follows(见 user1085093 答案),并将其作为底层视图添加到 ipad 屏幕。

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

// If not dragging, send event to next responder
if (!self.dragging)
    [self.nextResponder touchesBegan: touches withEvent:event];

else
    [super touchesBegan: touches withEvent: event];


-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

// If not dragging, send event to next responder
if (!self.dragging)
    [self.nextResponder touchesMoved: touches withEvent:event];

else
    [super touchesMoved: touches withEvent: event];



-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

// If not dragging, send event to next responder
if (!self.dragging)
    [self.nextResponder touchesEnded: touches withEvent:event];

else
    [super touchesEnded: touches withEvent: event];

我是这样设置滚动视图的:

 TopLayerScrollView *newScrollView = [[TopLayerScrollView alloc] init];
[newScrollView setBackgroundColor:[UIColor clearColor]];
[newScrollView setFrame:self.tabBarController.view.frame];
[newScrollView setContentSize:dualScreenViewController.scrollContent.contentSize];
newScrollView.showsHorizontalScrollIndicator = NO;
newScrollView.showsVerticalScrollIndicator = NO;
newScrollView.delegate = self;
newScrollView.bounces = NO;
[newScrollView scrollsToTop];
newScrollView.panGestureRecognizer.minimumNumberOfTouches = 2;

self.topLayerScrollView = newScrollView;
[newScrollView release];

[self.tabBarController.view removeFromSuperview];
[topLayerScrollView addSubview:self.tabBarController.view];
[window addSubview:topLayerScrollView];
[topLayerScrollView bringSubviewToFront:self.tabBarController.view];

底层滚动视图的委托方法:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
if (scrollView.dragging || scrollView.tracking)

    [dualScreenViewControlleremphasized text.scrollContent setContentOffset:CGPointMake(scrollView.contentOffset.x, scrollView.contentOffset.y) animated:NO];
    self.tabBarController.view.frame = CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y, self.tabBarController.view.frame.size.width, self.tabBarController.view.frame.size.height);

此解决方案效果很好。 另一种解决方案是将滚动视图作为我最初打算的覆盖视图。如前所述,问题是让顶层视图知道它下面的视图(nextResponder)。要实现这一点,您必须继承 UIScrollview 并创建一个 UIResponder-property,您必须在您的 interface-builder 文件中或在运行时进行连接。这样,覆盖的滚动视图将知道谁是下一个响应者。 please see morningstar's answer

【讨论】:

请参阅developer.apple.com/videos/play/wwdc2012/223,其中 Josh Shaffer “真的不鼓励”我们手动调用下一个响应者或其他对象的 touches... 方法。该视频中还有一个解决方案。【参考方案2】:

很遗憾,您无法在此处对-pointInside:withEvent: 执行任何操作,因为系统在确定触摸是否满足手势识别器的要求之前,会先决定触摸在哪个视图(或触摸在哪个视图)。

我认为您最好的选择是让当前位于滚动视图后面的视图实际上是滚动视图的内容视图,并在每次滚动视图的内容偏移量发生变化时移动该视图以使其保持不变在屏幕上的位置。 (这听起来很昂贵,但实际上并非如此。)因此,您的滚动视图更像是一个底图,而不是覆盖层。

您可能还希望使用UIScrollView.delaysContentTouches-[UIScrollView touchesShouldBegin:withEvent:inContentView:] 等来优化行为。

【讨论】:

非常感谢您。我添加了一个解释,我最终是如何解决这个问题的。【参考方案3】:

Xcode 6.4、Swift 1.2 更新:

我还继承了UIScrollView

import UIKit

class WBMScrollView: UIScrollView

    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent)
    
        self.nextResponder()?.touchesBegan(touches, withEvent: event)
        super.touchesBegan(touches, withEvent: event)
    

在app main vc ViewController.swift我测试过:

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) 
        println(" touches began")
    

它奏效了。滚动视图已通过 IB 添加并连接到 ViewController.swift

【讨论】:

【参考方案4】:

在我的应用中,我需要将单指触摸事件转发到滚动视图的父视图控制器,同时仍允许滚动视图放大/缩小。

使用下一个响应者不起作用,因为我还添加了另一个覆盖视图,最终成为下一个响应者并窃取了触摸事件。通过简单地传入父视图控制器并将触摸事件直接发送给它,我最终根据汤姆的回答做了一些事情。这是在 Swift 中的:

class CustomScrollView: UIScrollView 

    var parentViewController: UIViewController?

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 
        parentViewController?.touchesBegan(touches, with: event)
    

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) 
        parentViewController?.touchesMoved(touches, with: event)
    

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) 
        parentViewController?.touchesEnded(touches, with: event)
    

【讨论】:

以上是关于从 UIScrollview 向前触摸和手势到视图的主要内容,如果未能解决你的问题,请参考以下文章

带有检测触摸的图像视图的 UIScrollView

UIScrollView 并检测其子视图的手势

iOS 不会将触摸转发到以下视图

UIScrollview 对任何触摸都没有反应?

将捏合手势从自定义 UIView 传递到父 UIScrollView

如何将触摸事件从一个视图传递到另一个视图