UITableView 的水平移动?

Posted

技术标签:

【中文标题】UITableView 的水平移动?【英文标题】:Horizontal movement of a UITableView? 【发布时间】:2011-07-09 19:04:43 【问题描述】:

我试图区分水平滑动/平移和 UITableView 中的垂直滚动。我正在寻找的行为 模仿(在某种意义上)是 Twitter iPad 应用程序的模仿,它具有 可以在屏幕上水平移动的多个UITableView。如果 我在其中一个UITableView 上向左或向右滑动手指,即视图 本身水平移动。如果我垂直滑动,视图滚动为 预计。

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

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

谢谢。

【问题讨论】:

【参考方案1】:

几天来,我一直在为类似的问题苦苦挣扎,并尝试了几种可能的解决方案。我找到了将 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

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

在我的 UITableView 初始化中:

horizontalPanGesture = [[TableViewCellPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleHorizontalDrag:)];
[self addGestureRecognizer:horizontalPanGesture];
[horizontalPanGesture release];

以及动作方法:

-(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;

    

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

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

这个解决方案虽然简单,但我花了一段时间才完全满足我的确切需求。我对 ios 开发还很陌生,所以我不得不花大量时间阅读和修改手势识别器和滚动视图来解决这个问题。

【讨论】:

这是一个很好的答案,值得注意的是,此版本仅支持一个方向,因为您忽略了视图的方向,将 fromView:nil 切换为 fromView:self.view 将考虑旋转。 你能补充一些关于这个方法的信息吗?看起来您的 handleHorizontalDrag: 方法在您的 UITableView 子类中,但它引用了 TableViewCellPanGestureRecognizer 中的一个属性,并且在不清楚它的声明位置时还引用了另一个属性 (touchedCell)。【参考方案2】:

我自己从来没有这样做过,但是作为 UITableView 是 UIScrollView 的子类,UITableView 的代表也是 UIScrollViewDelegates。所以在你的 UITableViewController 子类中,你应该能够使用 UIScrollView 委托,并拦截滚动 - 确保也调用超级方法。

【讨论】:

在这种情况下,让滚动视图告诉我它何时即将移动或移动后对我没有帮助。我需要在做出滚动决定之前的信息。 嗯……你可以使用 UIPanGestureRecognizer。设置方向为左/右,并根据手势状态的变化移动表格视图。 UIPanGestureRecognizer 不允许您限制方向,AFAIK。 UISwipeGestureRecognizer 怎么样? UISwipeGestureRecognizer 检测到滑动。我需要不断更新位置。【参考方案3】:

如果您希望 UITableViews “并排”放置,并且当您水平滑动时,您希望它们全部同时水平移动,(例如带有 UITableViews 而不是图像的照片库),您可以执行以下操作:

使用 UIScrollView 并将 UITableViews 添加为 UIScrollView 的子视图。您应该像这样设置滚动视图的 contentSize:

CGRect bounds = scrollView.bounds;
scrollView.contentSize=CGSizeMake(bounds.size.width * kNumOfTableViews,  bounds.size.height);

让 UIScrollview 水平滚动而不是垂直滚动。

您可能还想使用

scrollView.pagingEnabled=YES;

取决于理想的行为。

如果您垂直滑动手指,UITableviews 会以正常方式响应,并且您可以通过水平滑动手指在 UITableViews 之间切换。

有关如何有效执行此操作的更多详细信息,您可以观看 WWDC 2010 视频 Session 104 - Designing Apps with Scroll Views 并从此处查看源代码:http://developer.apple.com/library/ios/#samplecode/PhotoScroller/。本节介绍如何在图像之间滑动。你将使用 UITableViews 而不是图像

但是,如果您希望每个 UITableView 能够独立水平移动,并且可能与另一个 UITableView 重叠,就像在 iPad 的 twitter 应用程序中那样,那么这个解决方案将不适合您,至少不能开箱即用。

【讨论】:

以上是关于UITableView 的水平移动?的主要内容,如果未能解决你的问题,请参考以下文章

如何检测scrollView的滚动方向?

不调用UITableView Delegate

不要删除 UITable 的第一行

将子视图添加到 UITable

可滚动且与 UITable 滚动同步的背景图像

更改 UITable 部分背景颜色而不丢失部分标题