2012-06-13 21 views
3

這裏是UIPanGesture子類:爲什麼我的平移手勢只會推動它的視線並停止?

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
    NSLog(@"touches began"); 
    self ->origLoc = [[touches anyObject] locationInView:self.view.superview]; 
    [super touchesBegan:touches withEvent:event]; 
} 


- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 
if (self.state == UIGestureRecognizerStatePossible) { 
    CGPoint loc = [[touches anyObject] locationInView:self.view.superview]; 
    CGFloat deltaX = fabsf(loc.x - origLoc.x); 
    CGFloat deltaY = fabsf(loc.y - origLoc.y); 
    if (deltaY >= deltaX) { 
     self.state = UIGestureRecognizerStateFailed; 
    } else { 
    [super touchesMoved:touches withEvent:event]; 
    } 
} 
} 

- (CGPoint) translationInView:(UIView *)v { 

CGPoint proposedTranslation = [super translationInView:v]; 
proposedTranslation.y = 0; 
return proposedTranslation; 

} 

這裏是被我的子類UIPanGesture調用的方法:

- (void)dragging: (UIPanGestureRecognizer *)p {  

UIView *vv = p.view; 

if (p.state == UIGestureRecognizerStateBegan || p.state == UIGestureRecognizerStateChanged) { 
    CGPoint delta = [p translationInView:vv.superview]; 
    CGPoint c = vv.center; 
    c.x += delta.x; 
    c.y += delta.y; 
    vv.center = c; 
    [p setTranslation:CGPointZero inView:vv.superview]; 
}  
} 

當我試圖拖動我看來,它只是移動一個非常小的量然後停下來。我需要不斷推動它才能讓它移動。有任何想法嗎?

在此先感謝,

Michael。

+0

danh可能正在走向正確的思路。出於好奇,你想用這個子類型的手勢識別器做什麼?如果你只是想左右搖擺,那麼解決這個問題有更簡單的方法。或者您是否還有其他一些處理垂直移動的平移手勢識別器,並且您正在嘗試檢測使用哪種手勢識別器?但是我會認爲,一旦你鎖定了一個特定的識別器,你就會停止進一步檢查滑動方向(以允許微小的手指移動進行微小的改變)。 – Rob

+0

它是在GMGridView(UIScrollview)中的單元格上進行操作的,我只想在檢測到水平平移時向左或向右移動其中一個單元格內的內容,並在檢測到垂直平移時滾動整個視圖。你提到我應該停止檢查滑動方向,我該怎麼做? –

+0

底線有一個變量,用於跟蹤您是否確認了是否檢查了初始動作的方向,還有另一個變量會告訴您它是否水平。然後,您可以檢查該變量是否跟蹤初始移動是否爲水平,以確定是否將狀態更改爲UIGestureRecognizerStateFailed。看到我的答案。 – Rob

回答

2

在我之前的評論,我不清楚爲什麼被繼承,但是你澄清,這是因爲你有多個手勢處理程序正在進行。很好。

所以這裏是一個自定義的平移手勢一些示例代碼,Horizo​​ntalPanGestureRecognizer,這也是引發只有當第一個運動是水平:

// HorizontalPanGestureRecognizer.h 

#import <UIKit/UIKit.h> 

@interface HorizontalPanGestureRecognizer : UIPanGestureRecognizer 
@end 

而且

// HorizontalPanGestureRecognizer.m 

#import "HorizontalPanGestureRecognizer.h" 
#import <UIKit/UIGestureRecognizerSubclass.h> 

@interface HorizontalPanGestureRecognizer() 
{ 
    BOOL _hasConfirmedDirection; 
    BOOL _wasHorizontal; 
    CGPoint _origLoc; 
} 
@end 

@implementation HorizontalPanGestureRecognizer 

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    [super touchesBegan:touches withEvent:event]; 

    UITouch *touch = [touches anyObject]; 

    _hasConfirmedDirection = NO; 

    _origLoc = [touch locationInView:self.view]; 
} 

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    [super touchesMoved:touches withEvent:event]; 
    if (self.state == UIGestureRecognizerStateFailed) return; 

    if (!_hasConfirmedDirection) 
    { 
     CGPoint translation = [self translationInView:self.view]; 

     _hasConfirmedDirection = YES; 
     _wasHorizontal = (fabs(translation.x) > fabs(translation.y)); 
    } 
    if (!_wasHorizontal) 
     self.state = UIGestureRecognizerStateFailed; 
} 

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    [super touchesEnded:touches withEvent:event]; 

    if (!_wasHorizontal) 
     self.state = UIGestureRecognizerStateFailed; 
} 

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    [super touchesCancelled:touches withEvent:event]; 

    if (!_wasHorizontal) 
     self.state = UIGestureRecognizerStateFailed; 
} 

- (CGPoint)locationInView:(UIView *)view 
{ 
    CGPoint superLocation = [super locationInView:view]; 

    if (_hasConfirmedDirection) 
     superLocation.y = _origLoc.y; 

    return superLocation; 
} 

- (CGPoint)translationInView:(UIView *)view 
{ 
    CGPoint superTranslation = [super translationInView:view]; 

    if (_hasConfirmedDirection) 
     superTranslation.y = 0.0f; 

    return superTranslation; 
} 

@end 

然後你可以有你處理程序在你的主視圖控制器中使用它(在這個例子中,只是拖動一個UILabel)。在viewDidLoad中,創建手勢:

HorizontalPanGestureRecognizer *recognizer = [[HorizontalPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleCustomPan:)]; 
[self.view addGestureRecognizer:recognizer]; 

然後處理程序看起來像:

- (void)handleCustomPan:(UIPanGestureRecognizer *)sender 
{  
    switch (sender.state) { 
     case UIGestureRecognizerStateChanged: 
      if (!_panLabel) 
      { 
       // In my case I'm creating a UILabel to drag around, whereas you might just drag 
       // whatever countrol you want to drag. 
       // 
       // But, regardless, I'm keeping track of the original center in _panLabelOrigCenter 

       [self makePanLabel:sender]; 
      } 

      CGPoint translate = [sender translationInView:self.view]; 
      _panLabel.center = CGPointMake(_panLabelOrigCenter.x + translate.x, _panLabelOrigCenter.y + translate.y); 

      break; 

     case UIGestureRecognizerStateEnded: 
      [self removePanLabel]; 
      break; 

     default: 
      break; 
    } 
} 

(很顯然,這是ARC代碼如果非ARC,添加必要的附加線。用於例行內存管理的代碼)。

+0

非常感謝! –

+0

嗨,羅布,我只是想使用上面,但得到兩個erros 1. 'UIPanGestureRecognizer'沒有明顯的@interface聲明選擇器'touchesEnded:withEvent:'2.分配只讀屬性你可以幫助你。 –

+1

@PavanMore你必須'#import '(這是一個頭文件,用於子類化手勢識別器,暴露一些私有的方法,以及使一些通常的'readonly'屬性成爲'readwrite') 。對不起,這不明確,我試圖澄清我的答案。 – Rob

0

接觸移動的失敗情況看起來像對早期運動會非常敏感。

考慮將它不太敏感...

- (BOOL)movementIsHorizontalX:(CGFloat)x andY:(CGFloat) y { 

    CGFloat absX = fabs(x); 
    CGFloat absY = fabs(y); 

    // if x is small, then accept small y values but not large ones 
    if (absX < 4) return absY < 4; 

    // now we can express horizontal-ness as a slope. we've ruled out small x, so this is a stable calculation 
    CGFloat slope = absY/absX; 
    return slope < 0.10; 

    // you could generalize by passing in the x threshold and percentage slope constants 
} 
+0

是的,這是有道理的。有沒有更好的地方來進行檢查?我希望視圖只接受水平移動並防止超視圖的垂直滾動(UIScrollView)。除非滾動最初是垂直的,否則可以滾動並且可以忽略水平移動。 –

+0

我認爲這是一個檢查的好地方,但我們可以改進測試。看我的編輯。 – danh

+0

感謝您的回覆。我現在有以下內容,請參閱下面的內容: –

0

我意識到自己的錯誤。我移動了

[super touchesMoved: touches withEvent:event]; 

以外的if語句,現在它似乎工作正常。不是100%確定它爲什麼現在可行,但我很高興。

我們弄清楚如何防止UIScrollView的自受理平移手勢,直到這一次取消/失敗...

+0

我的答案中包含的代碼處理泛如果水平啓動(並因此UITableView的手勢識別器沒有得到它)​​,但忽略它,並將其傳遞給tableview的標準手勢,如果垂直啓動。如果我誤解了你正在做的事,請告訴我。 – Rob

+0

就是這樣,謝謝羅伯特。 –