1

我想讓用戶抓住一個正在UIPanGestureRecognizer方法完成時動畫的UIView。我目前能夠使用touchesBegan和視圖的表示層來停止視圖並將其置於手指下方。但我希望用戶能夠重新觸發UIPanGestureRecognizer方法,而無需擡起手指並將其替換到UIView上。在UIView動畫時重新啓動UIPanGestureRecognizer

我一直在研究這個挑戰一個星期左右,並已通過各種搜索字符串在SO和其他地方,通過Ray Wenderlich的教程等閱讀,但仍未找到解決方案。它看起來像我試圖做同樣的事情對這個問題似乎沒有解決:

iOS - UIPanGestureRecognizer : drag during animation

我剛纔一直在與UIView的動畫塊和還沒有潛入CABasicAnimation工作。以某種方式改變下面的代碼可以做我想做的事嗎?

預先感謝您。

在FlingViewController.h:

#import <UIKit/UIKit.h> 

@interface FlingViewController : UIViewController <UIGestureRecognizerDelegate> 
{ 
    UIView *myView; 
} 

@property (nonatomic, weak) UIView *myView; 



@end 

在FlingViewController.m:

#import "FlingViewController.h" 
#import <QuartzCore/QuartzCore.h> 

@interface FlingViewController() 

@end 

@implementation FlingViewController 

@synthesize myView = _myView; 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    // Do any additional setup after loading the view, typically from a nib. 

    // Allocate and init view 
    myView = [[UIView alloc] initWithFrame:CGRectMake(self.view.center.x - 50, 
                 self.view.center.y - 50, 
                 100, 100)]; 

    // Set view background color 
    [myView setBackgroundColor:[UIColor purpleColor]]; 

    // Create pan gesture recognizer 
    UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] 
             initWithTarget:self action:@selector(handleGesture:)]; 

    // Add gesture recognizer to view 
    gesture.delegate = self; 
    [myView addGestureRecognizer:gesture]; 

    // Add myView as subview 
    [self.view addSubview:myView]; 
    NSLog(@"myView added"); 


} 

- (void)handleGesture:(UIPanGestureRecognizer *)recognizer 
{ 
    if ((recognizer.state == UIGestureRecognizerStateBegan) || 
     (recognizer.state == UIGestureRecognizerStateChanged)) 
    { 
     [recognizer.view.layer removeAllAnimations]; 

     CGPoint translation = [recognizer translationInView:self.view]; 
     recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, 
              recognizer.view.center.y + translation.y); 
     [recognizer setTranslation:CGPointMake(0, 0) inView:self.view]; 

    } 
    else if (recognizer.state == UIGestureRecognizerStateEnded) 
    { 
     CGPoint velocity = [recognizer velocityInView:self.view]; 
     CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y)); 
     CGFloat slideMult = magnitude/200; 

     float slideFactor = 0.1 * slideMult; 
     CGPoint finalPoint = CGPointMake(recognizer.view.center.x + (velocity.x * slideFactor), 
             recognizer.view.center.y + (velocity.y * slideFactor)); 
     finalPoint.x = MIN(MAX(finalPoint.x, 0), self.view.bounds.size.width); 
     finalPoint.y = MIN(MAX(finalPoint.y, 0), self.view.bounds.size.height); 

     [UIView animateWithDuration:slideFactor * 2 
           delay:0 
          options:(UIViewAnimationOptionCurveEaseOut| 
            UIViewAnimationOptionAllowUserInteraction | 
            UIViewAnimationOptionBeginFromCurrentState) 
         animations:^{ 
          recognizer.view.center = finalPoint; 
         } 
         completion:^(BOOL finished){ 
          NSLog(@"Animation complete"); 
         }]; 
    } 
} 


// Currently, this method will detect when user touches 
// myView's presentationLayer and will center that view under the finger 
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    UITouch *touch = [touches anyObject]; 
    CGPoint touchPoint = [touch locationInView:self.view]; 

    if ([[myView.layer presentationLayer] hitTest:touchPoint]) 
    { 
     NSLog(@"Touched!"); 

     [myView.layer removeAllAnimations]; 

     myView.center = touchPoint; 

     // *** NOTE *** 
     // At this point, I want the user to be able to fling 
     // the view around again without having to lift the finger 
     // and replace it on the view; i.e. when myView is moving 
     // and the user taps it, it should stop moving and follow 
     // user's pan gesture again 
     // Similar to unresolved SO question: 
     // https://stackoverflow.com/questions/13234234/ios-uipangesturerecognizer-drag-during-animation 

    } 
} 


- (void)didReceiveMemoryWarning 
{ 
    [super didReceiveMemoryWarning]; 
    // Dispose of any resources that can be recreated. 
} 

@end 

回答

0

在情況下,它可以幫助別人,我發現,似乎工作得很好的解決方案的基礎上,托馬斯後悔/傑克的這個太問題迴應:

CAAnimation - User Input Disabled

我使用兩個UIPanGestureRecognizers移動UIView - 一個(手勢/ handleGesture :)連接到UIView(myView),另一個(bigGesture/superGesture :)連接到該UIView的superview(bigView)。超級視圖在動畫時檢測子視圖表示層的位置。

目前唯一的問題是用戶仍然可以通過在動畫完成之前觸摸myView動畫的結束位置來獲取動畫視圖。理想情況下,只有表示層位置纔是用戶可以與視圖進行交互的地方。如果有人對防止過早交互有所瞭解,那將是值得讚賞的。

#import "FlingViewController.h" 
#import <QuartzCore/QuartzCore.h> 

@interface FlingViewController() 

@end 

@implementation FlingViewController 

@synthesize myView = _myView; 
@synthesize bigView = _bigView; 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    // Do any additional setup after loading the view, typically from a nib. 

    // *** USING A GESTURE RECOGNIZER ATTACHED TO THE SUPERVIEW APPROACH 
    // CAME FROM SO USER THOMAS RUED/JACK AT FOLLOWING LINK: 
    // https://stackoverflow.com/questions/7221688/caanimation-user-input-disabled 

    // Set up size for superView (bigView) 
    CGRect screenRect = [[UIScreen mainScreen] bounds]; 

    // Allocate big view 
    bigView = [[UIView alloc] initWithFrame:(screenRect)]; 

    [bigView setBackgroundColor:[UIColor greenColor]]; 

    [self.view addSubview:bigView]; 

    // Allocate and init myView 
    myView = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 100, 100)]; 

    // Set view background color 
    [myView setBackgroundColor:[UIColor purpleColor]]; 

    // Create pan gesture recognizer 
    UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] 
             initWithTarget:self action:@selector(handleGesture:)]; 

    UIPanGestureRecognizer *bigGesture = [[UIPanGestureRecognizer alloc] 
              initWithTarget:self action:@selector(superGesture:)]; 

    // Add gesture recognizer to view 
    gesture.delegate = self; 
    bigGesture.delegate = self; 
    [myView addGestureRecognizer:gesture]; 
    [bigView addGestureRecognizer:bigGesture]; 

    // Add myView as subview of bigView 
    [bigView addSubview:myView]; 
    NSLog(@"myView added"); 

} 

// This gesture recognizer is attached to the superView, 
// and will detect myView's presentation layer while animated 
- (void)superGesture:(UIPanGestureRecognizer *)recognizer 
{  
    CGPoint location = [recognizer locationInView:self.view]; 

    for (UIView* childView in recognizer.view.subviews) 
    { 
     CGRect frame = [[childView.layer presentationLayer] frame]; 

     if (CGRectContainsPoint(frame, location)) 
     // ALTERNATE 'IF' STATEMENT; BOTH SEEM TO WORK: 
     //if ([[childView.layer presentationLayer] hitTest:location]) 
     { 
      NSLog(@"location = %.2f, %.2f", location.x, location.y); 
      [childView.layer removeAllAnimations]; 
      childView.center = location; 
     } 

     if ((recognizer.state == UIGestureRecognizerStateEnded) && 
      (CGRectContainsPoint(frame, location))) 
     { 
      CGPoint velocity = [recognizer velocityInView:self.view]; 
      CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y)); 
      CGFloat slideMult = magnitude/200; 

      float slideFactor = 0.1 * slideMult; 
      CGPoint finalPoint = CGPointMake(childView.center.x + (velocity.x * slideFactor), 
              childView.center.y + (velocity.y * slideFactor)); 
      finalPoint.x = MIN(MAX(finalPoint.x, 0), self.view.bounds.size.width); 
      finalPoint.y = MIN(MAX(finalPoint.y, 0), self.view.bounds.size.height); 

      [UIView animateWithDuration:slideFactor * 2 
            delay:0 
           options:(UIViewAnimationOptionCurveEaseOut| 
             UIViewAnimationOptionAllowUserInteraction | 
             UIViewAnimationOptionBeginFromCurrentState) 
          animations:^{ 
           childView.center = finalPoint; 
          } 
          completion:^(BOOL finished){ 
           NSLog(@"Big animation complete"); 
          }]; 
     } 
    } 
} 

// This gesture recognizer is attached to myView, 
// and will handle movement when myView is not animating 
- (void)handleGesture:(UIPanGestureRecognizer *)recognizer 
{ 

    if ((recognizer.state == UIGestureRecognizerStateBegan) || 
     (recognizer.state == UIGestureRecognizerStateChanged)) 
    { 
     [recognizer.view.layer removeAllAnimations]; 

     CGPoint translation = [recognizer translationInView:self.view]; 
     recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, 
              recognizer.view.center.y + translation.y); 
     [recognizer setTranslation:CGPointMake(0, 0) inView:self.view]; 

    } 
    else if (recognizer.state == UIGestureRecognizerStateEnded) 
    { 
     CGPoint velocity = [recognizer velocityInView:self.view]; 
     CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y)); 
     CGFloat slideMult = magnitude/200; 

     float slideFactor = 0.1 * slideMult; 
     CGPoint finalPoint = CGPointMake(recognizer.view.center.x + (velocity.x * slideFactor), 
             recognizer.view.center.y + (velocity.y * slideFactor)); 
     finalPoint.x = MIN(MAX(finalPoint.x, 0), self.view.bounds.size.width); 
     finalPoint.y = MIN(MAX(finalPoint.y, 0), self.view.bounds.size.height); 

     [UIView animateWithDuration:slideFactor * 2 
           delay:0 
          options:(UIViewAnimationOptionCurveEaseOut| 
            UIViewAnimationOptionAllowUserInteraction | 
            UIViewAnimationOptionBeginFromCurrentState) 
         animations:^{ 
          recognizer.view.center = finalPoint; 
         } 
         completion:^(BOOL finished){ 
          NSLog(@"Animation complete"); 
         }]; 
    } 

} 

- (void)didReceiveMemoryWarning 
{ 
    [super didReceiveMemoryWarning]; 
    // Dispose of any resources that can be recreated. 
} 

@end