几天来,我一直在为类似的问题苦苦挣扎,并尝试了几种可能的解决方案。我找到了将 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 开发还很陌生,所以我不得不花大量时间阅读和修改手势识别器和滚动视图来解决这个问题。