【问题标题】:How to properly forward UITouch events in responder chain?如何在响应者链中正确转发 UITouch 事件?
【发布时间】:2010-06-29 09:22:43
【问题描述】:

我正在努力解决以下问题。我有一个带有滚动视图的视图控制器。使用这个滚动视图,用户应该能够滑动浏览一组“页面”——就像在苹果的天气应用程序中一样,所以没什么不寻常的。然后滚动视图中的每个“页面”都是表格视图,显示项目列表。在这里,我正在解决问题。

问题是滚动视图拦截所有触摸以查看用户是否移动了他的手指或只是点击了某个子视图内的某个点,并且它不会将事件传递给它的子视图(或超级视图)以防它识别到移动。

在我的情况下,这种行为导致了一个真正的问题(滚动视图中的表视图),因为表视图是滚动视图的后代,并且它是在正常情况下通过命中测试返回的视图,这会导致所有事件都是被表格视图抓取,所以它的父滚动视图永远不会被调用 - 所以不会执行滑动。

当然,当我在我的表格视图子类中重写 hitTest:withEvent: 方法并返回一个超级视图(实际上是滚动视图)时,情况正好相反——用户可以滑动页面,但不能滚动表格视图。

所以我需要弄清楚的是如何 - 在任何级别 - 确定用户想要采取的操作(滑动或向下/向上滚动表格视图),然后将事件流传递给适当的组件。

这几天我一直在与这个问题作斗争,但无法克服。以下是我到目前为止结束的内容。这里非常简化。它几乎可以满足我的需要 - 除了表格视图从不滚动...请注意,我的表格视图子类始终返回 self.superview,即滚动视图。

/// UIViewController

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

    [super touchesBegan: touches withEvent: event];

    /// Save initial touch location for later use
    _motionStart = [[touches anyObject] locationInView: self.scrollView];

    /// Save reference to initial event
    _initialEvent = event;

    /// Fire timer
    _gestureTimer = [NSTimer scheduledTimerWithTimeInterval: 0.2
                                 target:self selector:@selector(gestureTimer:)
                               userInfo: touches repeats: NO];
}

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

    if(!_gestureTimer && !_forwardingTouchesToTableView) {
    /// If no timer is running and no event forwarding is performed, pass to super
        [super touchesMoved: touches withEvent: event];
    }
    else {
        /// Pass event to currently visible tableview
        [self.scrollView.centerPage touchesMoved: touches withEvent: event];
    }

}

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

    [super touchesEnded: touches withEvent: event];

    if(_forwardingTouchesToTableView) {
        [self.scrollView.centerPage touchesEnded: touches withEvent: event];
    }

    /// Reset state
    _motionStart = CGPointZero;

    _forwardingTouchesToTableView = NO;
}

- (void) gestureTimer: (NSTimer *) timer {

    _gestureTimer = nil;

    NSSet *touches = (NSSet *)timer.userInfo;   
    UITouch *touch = (UITouch *)[touches anyObject];

    CGPoint currentTouchLocation = [touch locationInView: self.scrollView];

    float deltaX = fabs(_motionStart.x - currentTouchLocation.x);
    float deltaY = fabs(_motionStart.y - currentTouchLocation.y);

    if(deltaY >= 4.0 && deltaX <= 12.0) {

        /// Vertical move, forward to tableview
        [self.scrollView cancelTouches];
        _forwardingTouchesToTableView = YES;
        [self.scrollView.centerPage touchesBegan: touches  withEvent: _initialEvent];
        _initialEvent = nil;
    }
    else {
        /// Horizontal swipe - just reset state
        _initialEvent = nil;
        _forwardingTouchesToTableView = NO;
    }
}

说实话,我还不知道下一步该做什么。对于我发现的这个问题,我尝试了很多解决方案,但它们都没有真正奏效。我将非常感谢任何关于此的建议或提示。或许有人能解释一下 UIScrollView 或 UITableView 是如何处理事件的?

感谢您的任何建议。

【问题讨论】:

    标签: iphone uitableview event-handling uiscrollview


    【解决方案1】:

    所以基本上你想在表格视图中垂直滚动,但为水平滑动分页?

    如果您可以忍受 3.2+ 的兼容性(实际上是在 iPhone 4.0 上),我会尝试: - 坚持使用标准组件,没有花哨的额外处理 - 使用 UISwipeGestureRecognizer 并将其添加到您的表格视图中 - 如果手势识别器触发,则检查它是否是水平滑动,如果是,则以编程方式更改页面

    当然,整个概念只有在不需要单元格上的滑动手势时才有效 - 即用于删除。但这当然适用于所有方法。

    在 3.2 之前,您确实需要用触摸处理程序弄湿您的手。子类化 UITableView 和覆盖 touchesBegan: 等。我会第一次尝试(并识别水平滑动并调用分页),尽管整个解决方案很丑陋。

    【讨论】:

    • 感谢您的建议,Eiko,但不幸的是应用程序必须与 3.0+ 兼容,所以这就是我搞砸这些东西的原因。我试图实现 touchesBegan:... and co.在 UITableView、UIScrollView 和 UIViewController 的自定义子类中,但到目前为止还没有运气。 UIScrollView 文档提到它使用计时器来检查它是否应该开始“拖动”传递触摸到底层子视图,这基本上也是我需要做的,除了向它添加水平/垂直手势识别。我想知道它是如何在 UIScrollView 中实现的!
    猜你喜欢
    • 2020-05-15
    • 2017-09-15
    • 2018-07-07
    • 2014-12-31
    • 2012-09-08
    • 1970-01-01
    • 1970-01-01
    • 2011-01-15
    • 2023-02-14
    相关资源
    最近更新 更多