如何使用 UIPanGestureRecognizer 移动对象? iPhone/iPad
Posted
技术标签:
【中文标题】如何使用 UIPanGestureRecognizer 移动对象? iPhone/iPad【英文标题】:How to use UIPanGestureRecognizer to move object? iPhone/iPad 【发布时间】:2011-10-04 02:24:21 【问题描述】:UIPanGestureRecognizer
类有几个示例。比如我读过this,还是不能用……
在我正在处理的 nib 文件上,我有一个 UIView
(图像上的白色矩形),我希望用该类拖动它:
在我放置的 .m 文件中:
- (void)setTranslation:(CGPoint)translation inView:(UIView *)view
NSLog(@"Test to see if this method gets executed");
当我将鼠标拖过UIView
时,该方法不会被执行。我也试过放置:
- (void)pan:(UIPanGestureRecognizer *)gesture
NSLog(@"testing");
而且该方法也不会被执行。也许我错了,但我认为这种方法应该像 - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
方法一样工作,我只需放置该方法,只要有触摸它就会被调用。
我做错了什么?也许我必须与该方法建立联系?如果是这样,我该怎么做?
【问题讨论】:
【参考方案1】:我找到了教程Working with UIGestureRecognizers,我认为这就是我要找的。它帮助我想出了以下解决方案:
-(IBAction) someMethod
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(move:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[ViewMain addGestureRecognizer:panRecognizer];
[panRecognizer release];
-(void)move:(UIPanGestureRecognizer*)sender
[self.view bringSubviewToFront:sender.view];
CGPoint translatedPoint = [sender translationInView:sender.view.superview];
if (sender.state == UIGestureRecognizerStateBegan)
firstX = sender.view.center.x;
firstY = sender.view.center.y;
translatedPoint = CGPointMake(sender.view.center.x+translatedPoint.x, sender.view.center.y+translatedPoint.y);
[sender.view setCenter:translatedPoint];
[sender setTranslation:CGPointZero inView:sender.view];
if (sender.state == UIGestureRecognizerStateEnded)
CGFloat velocityX = (0.2*[sender velocityInView:self.view].x);
CGFloat velocityY = (0.2*[sender velocityInView:self.view].y);
CGFloat finalX = translatedPoint.x + velocityX;
CGFloat finalY = translatedPoint.y + velocityY;// translatedPoint.y + (.35*[(UIPanGestureRecognizer*)sender velocityInView:self.view].y);
if (finalX < 0)
finalX = 0;
else if (finalX > self.view.frame.size.width)
finalX = self.view.frame.size.width;
if (finalY < 50) // to avoid status bar
finalY = 50;
else if (finalY > self.view.frame.size.height)
finalY = self.view.frame.size.height;
CGFloat animationDuration = (ABS(velocityX)*.0002)+.2;
NSLog(@"the duration is: %f", animationDuration);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationDidFinish)];
[[sender view] setCenter:CGPointMake(finalX, finalY)];
[UIView commitAnimations];
【讨论】:
太棒了!我喜欢animationDuration的计算,效果很好。 如果您将 move:(id)sender 更改为 move:(UIPanGestureRecognizer*)sender,您就不必进行所有丑陋的类型转换。 此解决方案不适用于 UISwitch。我还尝试实现 touchesBegan、touchesMoved 方法但无法移动 UISwitch。你能告诉我如何仅通过拖动来移动 UISwitch 吗?? 记得为手势识别器关联的视图设置 userInteractionEnabled=YES。 什么是firstX和firstY?【参考方案2】:UIPanGestureRecognizer * pan1 = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(moveObject:)];
pan1.minimumNumberOfTouches = 1;
[image1 addGestureRecognizer:pan1];
-(void)moveObject:(UIPanGestureRecognizer *)pan;
image1.center = [pan locationInView:image1.superview];
【讨论】:
完美的胜利! 不能与简单性争论,但这个解决方案有一个问题:一旦你放下手指开始手势,image1
会跳,可能很远,所以它的中心在你的手指下。最好通过pan
的翻译调整image1.center
,以获得更流畅和标准的体验。【参考方案3】:
-(IBAction)Method
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
[ViewMain addGestureRecognizer:panRecognizer];
[panRecognizer release];
- (Void)handlePan:(UIPanGestureRecognizer *)recognizer
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];
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;
NSLog(@"magnitude: %f, slideMult: %f", magnitude, slideMult);
float slideFactor = 0.1 * slideMult; // Increase for more of a slide
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 animations:^
recognizer.view.center = finalPoint;
completion:nil];
【讨论】:
对于CGPointMake(0, 0)
,你可以写CGRectZero
。
@meaning-matters 你不是说CGPointZero
吗?
@mylogon 你说得对。我会把你的一些答案作为我的谢意。【参考方案4】:
if ([recognizer state] == UIGestureRecognizerStateChanged)
CGPoint translation1 = [recognizer translationInView:main_view];
img12.center=CGPointMake(img12.center.x+translation1.x, img12.center.y+ translation1.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:main_view];
recognizer.view.center=CGPointMake(recognizer.view.center.x+translation1.x, recognizer.view.center.y+ translation1.y);
-(void)move:(UIPanGestureRecognizer*)recognizer
if ([recognizer state] == UIGestureRecognizerStateChanged)
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];
【讨论】:
【参考方案5】:@MS AppTech 答案的 swift 2 版本
func handlePan(panGest : UIPanGestureRecognizer)
let velocity : CGPoint = panGest.velocityInView(self.view);
let magnitude : CGFloat = CGFloat(sqrtf((Float(velocity.x) * Float(velocity.x)) + (Float(velocity.y) * Float(velocity.y))));
let slideMult :CGFloat = magnitude / 200;
//NSLog(@"magnitude: %f, slideMult: %f", magnitude, slideMult);
let slideFactor : CGFloat = 0.1 * slideMult; // Increase for more of a slide
var finalPoint : CGPoint = CGPointMake(panGest.view!.center.x + (velocity.x * slideFactor),
panGest.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(Double(slideFactor*2), delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations:
panGest.view!.center = finalPoint;
, completion: nil)
【讨论】:
【参考方案6】:@Tono Nam 在 Swift 5 中的回答(已接受)
func someMethod()
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(move))
panGestureRecognizer.minimumNumberOfTouches = 1
panGestureRecognizer.maximumNumberOfTouches = 1
view.addGestureRecognizer(panGestureRecognizer)
@objc func move(sender: UIPanGestureRecognizer)
guard let senderView = sender.view else return
self.view.bringSubviewToFront(senderView)
var translatedPoint: CGPoint = sender.translation(in: senderView.superview)
if(sender.state == .began)
let firstX = senderView.center.x
let firstY = senderView.center.y
translatedPoint = CGPoint(x: senderView.center.x + translatedPoint.x, y: senderView.center.y + translatedPoint.y)
senderView.center = translatedPoint
sender.setTranslation(.zero, in: senderView)
if(sender.state == .ended)
let velocityX: CGFloat = (0.2 * sender.velocity(in: self.view).x)
let velocityY: CGFloat = (0.2 * sender.velocity(in: self.view).y)
var finalX: CGFloat = translatedPoint.x + velocityX
var finalY: CGFloat = translatedPoint.y + velocityY
if(finalX < 0)
finalX = 0
else if (finalX > self.view.frame.size.width)
finalX = self.view.frame.size.width;
if (finalY < 50) // to avoid status bar
finalY = 50;
else if (finalY > self.view.frame.size.height)
finalY = self.view.frame.size.height;
let animationDuration: TimeInterval = TimeInterval(abs(velocityX) * 0.0002 + 0.2)
print("the animation duration is \(animationDuration)")
UIView.animate(withDuration: animationDuration, delay: 0, options: .curveEaseOut)
senderView.center = CGPoint(x: finalX, y: finalY)
completion: completed in
// call animationDidFinish completion handler here
【讨论】:
【参考方案7】:几年后把我的帽子扔进戒指。
需要保存图像视图的开始中心:
var panBegin: CGPoint.zero
然后使用变换更新新中心:
if recognizer.state == .began
panBegin = imageView!.center
else if recognizer.state == .ended
panBegin = CGPoint.zero
else if recognizer.state == .changed
let translation = recognizer.translation(in: view)
let panOffsetTransform = CGAffineTransform( translationX: translation.x, y: translation.y)
imageView!.center = panBegin.applying(panOffsetTransform)
【讨论】:
【参考方案8】:Swift 2 版本:
// start detecting pan gesture
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(TTAltimeterDetailViewController.panGestureDetected(_:)))
panGestureRecognizer.minimumNumberOfTouches = 1
self.chartOverlayView.addGestureRecognizer(panGestureRecognizer)
func panGestureDetected(panGestureRecognizer: UIPanGestureRecognizer)
print("pan gesture recognized")
【讨论】:
以上是关于如何使用 UIPanGestureRecognizer 移动对象? iPhone/iPad的主要内容,如果未能解决你的问题,请参考以下文章
如何在自动布局中使用约束标识符以及如何使用标识符更改约束? [迅速]