在 UIView 动画时重新启动 UIPanGestureRecognizer
Posted
技术标签:
【中文标题】在 UIView 动画时重新启动 UIPanGestureRecognizer【英文标题】:Restarting UIPanGestureRecognizer while UIView is animating 【发布时间】:2013-07-22 16:43:22 【问题描述】:我试图让用户在完成 UIPanGestureRecognizer 方法时获取正在动画的 UIView。我目前能够使用 touchesBegan 和视图的表示层来停止视图并将其居中放置在手指下方。但我希望用户能够重新触发 UIPanGestureRecognizer 方法,而无需抬起手指并在 UIView 上替换它。
我已经为这个挑战工作了一周左右,并且在 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://***.com/questions/13234234/ios-uipangesturerecognizer-drag-during-animation
- (void)didReceiveMemoryWarning
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
@end
【问题讨论】:
【参考方案1】:如果它对其他人有帮助,根据 Thomas Rued/Jack 对这个 SO 问题的回答,我找到了一个似乎效果很好的解决方案:
CAAnimation - User Input Disabled
我正在使用两个 UIPanGestureRecognizer 来移动 UIView - 一个 (gesture/handleGesture:) 连接到 UIView (myView),另一个 (bigGesture/superGesture:) 连接到该 UIView 的超级视图 (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://***.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
【讨论】:
以上是关于在 UIView 动画时重新启动 UIPanGestureRecognizer的主要内容,如果未能解决你的问题,请参考以下文章
如何在更改 UIView 大小时应用动画? + 斯威夫特 5
如何为手动“动画”重新排队对 UIView drawRect 的请求