【问题标题】:Observing pinch multi-touch gestures in a UITableView在 UITableView 中观察捏合多点触控手势
【发布时间】:2011-01-01 11:18:53
【问题描述】:

我希望在 UITableView 上实现捏合/拉出,我已经研究了几种方法,包括这个:

Similar question

但是,虽然我可以创建一个 UIViewTouch 对象并将其覆盖到我的 UITableView 上,但滚动事件不会被中继到我的 UITableView,我仍然可以选择单元格,并且它们会通过触发到新 ViewController 对象的转换来正确响应。但是,尽管传递了 touchesBegan、touchesMoved 和 touchesEnded 事件,但我无法滚动 UITableView。

【问题讨论】:

    标签: iphone uitableview multi-touch pinch


    【解决方案1】:

    这似乎是一个经典问题。就我而言,我想通过 UIWebView 拦截一些不能被子类化的事件,等等。

    我发现最好的方法是使用 UIWindow 拦截事件:

    EventInterceptWindow.h

    @protocol EventInterceptWindowDelegate
    - (BOOL)interceptEvent:(UIEvent *)event; // return YES if event handled
    @end
    
    
    @interface EventInterceptWindow : UIWindow {
        // It would appear that using the variable name 'delegate' in any UI Kit
        // subclass is a really bad idea because it can occlude the same name in a
        // superclass and silently break things like autorotation.
        id <EventInterceptWindowDelegate> eventInterceptDelegate;
    }
    
    @property(nonatomic, assign)
        id <EventInterceptWindowDelegate> eventInterceptDelegate;
    
    @end
    

    EventInterceptWindow.m:

    #import "EventInterceptWindow.h"
    
    @implementation EventInterceptWindow
    
    @synthesize eventInterceptDelegate;
    
    - (void)sendEvent:(UIEvent *)event {
        if ([eventInterceptDelegate interceptEvent:event] == NO)
            [super sendEvent:event];
    }
    
    @end
    

    创建该类,将 MainWindow.xib 中的 UIWindow 类更改为 EventInterceptWindow,然后在某处将 eventInterceptDelegate 设置为要拦截事件的视图控制器。拦截双击的示例:

    - (BOOL)interceptEvent:(UIEvent *)event {
        NSSet *touches = [event allTouches];
        UITouch *oneTouch = [touches anyObject];
        UIView *touchView = [oneTouch view];
        //  NSLog(@"tap count = %d", [oneTouch tapCount]);
        // check for taps on the web view which really end up being dispatched to
        // a scroll view
        if (touchView && [touchView isDescendantOfView:webView]
                && touches && oneTouch.phase == UITouchPhaseBegan) {
            if ([oneTouch tapCount] == 2) {
                [self toggleScreenDecorations];
                return YES;
            }
        }   
        return NO;
    }
    

    这里的相关信息: http://iphoneincubator.com/blog/windows-views/360idev-iphone-developers-conference-presentation

    【讨论】:

    • 该方法效果很好,并且比覆盖对象方法更干净。谢谢!
    • 非常棒的解决方案,正在寻找这样的东西来为滚动视图/表格视图添加手势,谢谢! :)
    • 很棒的解决方案。我已经让它运行了几个月,现在捕捉到一个 3 指双击手势。最近,我的一个比较神经质的同事能够造成似乎是由此造成的崩溃。堆栈跟踪显示没有objective-c 代码,但是它在[UIScrollViewPanGestureRecognizer touchesCancelled:withEvent:] -[UIApplication _cancelTouches:withEvent:sendingTouchesCancelled:includingGestures:] 上抛出了EXEC_BAD_ACCESS。我的假设是第一个“3-tap”正在启动 PanGesture,但随后我只是终止了该事件(不是优雅地)。总是返回NO 修复它。
    • 上面的空间用完了......任何关于优雅取消的想法将不胜感激。
    • 参见stackoverflow.com/q/10403137/779419,了解如何在使用情节提要时使用自定义 UIWindow。
    【解决方案2】:

    尼姆罗德写道:

    在某处将 eventInterceptDelegate 设置为要拦截事件的视图控制器

    我没有立即理解这句话。为了其他和我有同样问题的人的利益,我这样做的方法是将以下代码添加到我的 UIView 子类中,该子类必须检测触摸。

    - (void) viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
    
        // Register to receive touch events
        MyApplicationAppDelegate *appDelegate = (MyApplicationAppDelegate *) [[UIApplication sharedApplication] delegate];
        EventInterceptWindow *window = (EventInterceptWindow *) appDelegate.window;
        window.eventInterceptDelegate = self;
    }
    
    
    - (void) viewWillDisappear:(BOOL) animated
    {
        // Deregister from receiving touch events
        MyApplicationAppDelegate *appDelegate = (MyApplicationAppDelegate *) [[UIApplication sharedApplication] delegate];
        EventInterceptWindow *window = (EventInterceptWindow *) appDelegate.window;
        window.eventInterceptDelegate = nil;
    
        [super viewWillDisappear:animated];
    }
    
    
    - (BOOL) interceptEvent:(UIEvent *) event
    {
        NSLog(@"interceptEvent is being called...");
        return NO;
    }
    


    这个版本的interceptEvent: 是捏到缩放检测的简单实现。注意。部分代码取自 Apress 的《开始 iPhone 3 开发》。

    CGFloat initialDistance;
    
    - (BOOL) interceptEvent:(UIEvent *) event
    {
        NSSet *touches = [event allTouches];
    
        // Give up if user wasn't using two fingers
        if([touches count] != 2) return NO;
    
        UITouchPhase phase = ((UITouch *) [touches anyObject]).phase;
        CGPoint firstPoint = [[[touches allObjects] objectAtIndex:0] locationInView:self.view];
        CGPoint secondPoint = [[[touches allObjects] objectAtIndex:1] locationInView:self.view];
    
        CGFloat deltaX = secondPoint.x - firstPoint.x;
        CGFloat deltaY = secondPoint.y - firstPoint.y;
        CGFloat distance = sqrt(deltaX*deltaX + deltaY*deltaY);
    
        if(phase == UITouchPhaseBegan)
        {
            initialDistance = distance;
        }
        else if(phase == UITouchPhaseMoved)
        {
            CGFloat currentDistance = distance;
            if(initialDistance == 0) initialDistance = currentDistance;
            else if(currentDistance - initialDistance > kMinimumPinchDelta) NSLog(@"Zoom in");
            else if(initialDistance - currentDistance > kMinimumPinchDelta) NSLog(@"Zoom out");
        }
        else if(phase == UITouchPhaseEnded)
        {
            initialDistance = 0;
        }
    
        return YES;
    }
    


    编辑:虽然这段代码在 iPhone 模拟器中 100% 运行良好,但当我在 iPhone 设备上运行它时,我遇到了与表格滚动相关的奇怪错误。如果您也遇到这种情况,请强制 interceptEvent: 方法在所有情况下都返回 NO。这意味着超类也会处理触摸事件,但幸运的是这并没有破坏我的代码。

    【讨论】:

    • 为什么不在 viewDidAppear 中使用self.view.window
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-05
    相关资源
    最近更新 更多