UITableView的水平移动?

jim*_*imt 7 uitableview ios

我试图区分水平滑动/平移和垂直滚动UITableView.我想要模仿的行为(在某种意义上)是Twitter iPad应用程序的行为,其中有多个UITableView可以在屏幕上水平移动.如果我在其中一个上向左或向右滑动手指UITableView,则视图本身会水平移动.如果我垂直滑动,视图会按预期滚动.

我无法找出实现此行为的正确方法.我已经看过一些关于此的教程,其中涉及在其中添加触摸事件处理程序UITableViewCells,并覆盖hitTestUITableView根据手势移动的方向适当地路由事件.我已经实现了一些这些技术,但它们都没有特别好用.

有谁知道实现这种行为的正确方法?有条件地UITableView依赖于用户手指移动的 方向执行动作?

谢谢.

Dar*_*man 9

几天来,我一直在努力解决类似问题,并且我已经经历了几个可能的解决方案.我找到了最好的方法,也是最简单的解决方案,可以将UIGestureRecognizer子类化,以处理水平移动并将其附加到UITableViews.

它的工作方式是它在进入UITableView(也是UIScrollView)之前拦截任何触摸事件.UITableView是UIScrollView的子类,它内置了一个自定义UIPanGestureRecognizer,可以检测拖动并相应地滚动它的视图.通过添加自己的UIGestureRecognizer子类,您可以在UIScrollView的手势识别器之前进行触摸.如果您的识别器看到用户正在水平拖动,它应该在覆盖的touchesMoved:方法中将其状态更改为UIGestureRecognizerStateBegan.否则,它将其状态设置为UIGestureRecognizerCancelled,这使得底层UIScrollView可以处理触摸.

这是我的UIGestureRecognizer子类的样子:

#import <UIKit/UIGestureRecognizerSubclass.h>

@interface TableViewCellPanGestureRecognizer : UIGestureRecognizer
{
    CGPoint startTouchPoint;
    CGPoint currentTouchPoint;
    BOOL isPanningHorizontally;
}

- (void)reset;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

@end

@implementation TableViewCellPanGestureRecognizer

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
    startTouchPoint = [[touches anyObject] locationInView:nil];
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
{
    [super touchesMoved:touches withEvent:event];
    currentTouchPoint = [[touches anyObject] locationInView:nil];

    if ( !isPanningHorizontally ) {

        float touchSlope = fabsf((currentTouchPoint.y - startTouchPoint.y) / (currentTouchPoint.x - startTouchPoint.x));

        if ( touchSlope < 1 ) {
            self.state = UIGestureRecognizerStateBegan;
            isPanningHorizontally = YES;
            [self.view touchesCancelled:touches withEvent:event];
        } else {
            self.state = UIGestureRecognizerStateCancelled;
            [self.view touchesCancelled:touches withEvent:event];
        }

    } else {
        self.state = UIGestureRecognizerStateChanged;
    }
}

-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesCancelled:touches withEvent:event];
    self.state = UIGestureRecognizerStateCancelled;
    [self.view touchesCancelled:touches withEvent:event];
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];
    self.state = UIGestureRecognizerStateCancelled;
}

-(void)reset
{
    [super reset];
    startTouchPoint = CGPointZero;
    currentTouchPoint = CGPointZero;
    isPanningHorizontally = NO;
}

@end
Run Code Online (Sandbox Code Playgroud)

然后我有一个子类UITableView,它将识别器附加到自身并实现一个动作方法来触发各行的水平移动:

在我的UITableView init中:

horizontalPanGesture = [[TableViewCellPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleHorizontalDrag:)];
[self addGestureRecognizer:horizontalPanGesture];
[horizontalPanGesture release];
Run Code Online (Sandbox Code Playgroud)

和动作方法:

-(void)handleHorizontalDrag:(UIGestureRecognizer *)gesture
{   
    UIGestureRecognizerState state = gesture.state;

    // Set the touched cell
    if (!touchedCell){
        NSIndexPath *indexPathAtHitPoint = [self indexPathForRowAtPoint:[gesture locationInView:self]];
        id cell = [self cellForRowAtIndexPath:indexPathAtHitPoint];
        touchedCell = cell;
        startTouchPoint = [gesture locationInView:touchedCell];
    }

    if ( state == UIGestureRecognizerStateBegan || state == UIGestureRecognizerStateChanged ) {

        // move your views horizontally          

    } else if ( state == UIGestureRecognizerStateEnded || state == UIGestureRecognizerStateCancelled ) {

        touchedCell = nil;

    }
}
Run Code Online (Sandbox Code Playgroud)

上面的内容会在表格视图中触摸当前单元格,然后在用户向左或向右拖动时对其应用水平移动.但是,如果我的手势识别器确定触摸是要垂直滚动,它只是取消自身,并且以下触摸被发送到UITableView以自动启动垂直滚动.

这个设置似乎比重写hitTest和在UITableView本身内做各种触摸事件欺骗要简单得多.它只是立即确定触摸运动的方向.您需要阅读UIGestureRecognizers - 特别是关于它应该如何被子类化.您需要确保将某些触摸事件(如touchesCancelled)转发到UITableView,因为UITableView内置的panGestureRecognizer不会像往常那样处理这些事件.显然,你想要移动整个表视图而不是单个单元格,但它应该非常简单.

这个简单的解决方案花了我一段时间才能完全满足我的确切需求.我对IOS开发很新,所以我不得不花很多时间阅读和修改手势识别器和滚动视图来解决这个问题.

  • 这是一个很好的答案,值得注意的是,这个版本只支持一个方向,因为你忽略了视图的方向,将你的fromView:nil切换为fromView:self.view将考虑轮换. (4认同)

Ben*_*ayo 0

我自己从来没有这样做过,但是由于 UITableView 是 UIScrollView 的子类,因此 UITableView 的委托也是 UIScrollViewDelegates。因此,在 UITableViewController 子类中,您应该能够使用 UIScrollView 委托,并拦截滚动 - 确保还调用 super 方法。