Reputation: 341
I'm trying to achieve dragging of view and when the drag stops, the view slide to top and disappear. I can use UIPanGestureRecognizer to drag the view fine. But after that, I used animation to let it slide out. The problem is that after the drag, there's always a little hiccup before the view moves... I searched around and can't figure out how to solve this. Here is the code:
- (void)moveView2:(UIPanGestureRecognizer *)pan {
CGPoint delta = [pan translationInView:self.view];
CGPoint newCenter = CGPointMake(_view2.center.x, _view2.center.y + delta.y);
_view2.center = newCenter;
[pan setTranslation:CGPointZero inView:self.view];
if (pan.state == UIGestureRecognizerStateEnded) {
CGPoint velocity = [pan velocityInView:self.view];
NSLog(@"Velocity is %f, %f", velocity.x, velocity.y);
// Here is the delay
[UIView animateWithDuration:0.5 delay:0.0 usingSpringWithDamping:0.5f initialSpringVelocity:500 options:UIViewAnimationOptionCurveEaseInOut animations:^{
_view2.center = CGPointMake(_view2.center.x, -600);
} completion:nil];
}
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *fbButton;
@property (weak, nonatomic) IBOutlet UIButton *twitterButton;
@property UIView *view1;
@property UIView *view2;
@property CGPoint startPoint;
@property CGPoint endPoint;
@property CGPoint originCenter;
@property double startTime;
@property double endTime;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CGRect frame = [UIScreen mainScreen].bounds;
_view1 = [[UIView alloc] initWithFrame:frame];
_view1.backgroundColor = [UIColor redColor];
[self.view addSubview:_view1];
_view2 = [[UIView alloc] initWithFrame:frame];
_view2.backgroundColor = [UIColor greenColor];
[self.view addSubview:_view2];
UIPanGestureRecognizer *pan1 = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveView2:)];
[_view2 addGestureRecognizer:pan1];
/*
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeUpDown:)];
[swipe setDirection:UISwipeGestureRecognizerDirectionUp];
[_view2 addGestureRecognizer:swipe];
*/
}
/*
- (void)swipeUpDown:(UISwipeGestureRecognizer *)swipe {
if (swipe.direction == UISwipeGestureRecognizerDirectionUp) {
NSLog(@"Swipe up");
[UIView animateWithDuration:0.5 animations:^{
_view2.center = CGPointMake(_view2.center.x, -600);
}];
}
}*/
- (void)moveView2:(UIPanGestureRecognizer *)pan {
CGPoint bigViewDelta = [pan translationInView:self.view];
CGPoint newCenter = CGPointMake(_view2.center.x, _view2.center.y + bigViewDelta.y);
_view2.center = newCenter;
[pan setTranslation:CGPointZero inView:self.view];
if (pan.state == UIGestureRecognizerStateEnded) {
CGPoint velocityOfPan = [pan velocityInView:self.view];
CGFloat velocityOfPanAbsolute = sqrt(velocityOfPan.x * velocityOfPan.x + velocityOfPan.y * velocityOfPan.y);
// get simple points per second
CGPoint currentPoint = _view2.center;
CGPoint finalPoint = CGPointMake(_view2.center.x, -600);
CGFloat distance = sqrt((finalPoint.x - currentPoint.x) * (finalPoint.x - currentPoint.x) + (finalPoint.y - currentPoint.y) * (finalPoint.y - currentPoint.y));
// how far to travel
CGFloat duration = 0.5;
CGFloat animationVelocity = velocityOfPanAbsolute / (distance / duration);
[UIView animateWithDuration:duration delay:0.0 usingSpringWithDamping:1.0 initialSpringVelocity:animationVelocity options:0 animations:^{
_view2.center = CGPointMake(_view2.center.x, -600);
} completion:nil]; }
}
- (void)viewDidAppear:(BOOL)animated {
/*
[UIView transitionFromView:_view2 toView:_view1 duration:2 options:UIViewAnimationOptionCurveEaseInOut completion:^(BOOL finished) {
}];
*/
/*
// get the view that's currently showing
UIView *currentView = _view2;
// get the the underlying UIWindow, or the view containing the current view view
UIView *theWindow = [currentView superview];
// remove the current view and replace with myView1
[currentView removeFromSuperview];
//[theWindow addSubview:newView];
// set up an animation for the transition between the views
CATransition *animation = [CATransition animation];
[animation setDuration:0.5];
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromLeft];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[[theWindow layer] addAnimation:animation forKey:@"SwitchToView1"];
*/
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
Upvotes: 1
Views: 1197
Reputation: 437392
Do you mean a delay before the animation takes place? Hmm, I'm not seeing anything that would cause that, but the parameters to animateWithDuration
are very, very curious.
First, UIViewAnimationOptionCurveEaseInOut
doesn't makes sense to me, because that's used to say "start slowly, pick up speed, and stop slowly, too". That's fine with a standard animation where you don't want the starting of the animation to be too jarring. But it doesn't make sense in this context because you presumably want it not to start slowly, but rather to use whatever velocity you initially provided. If I used any option
, I'd use UIViewAnimationOptionCurveEaseOut
. Even that doesn't make sense, because it's the usingSpringWithDamping
parameter that dictates what happens at the end of this animation. (And, on top of all of that, you're dragging it off screen, so I'm unclear why you care about the exit curve anyway.)
Second, the initialSpringVelocity
is doesn't seem at all right to me, either. As the documentation for this parameter says:
The initial spring velocity. For smooth start to the animation, match this value to the view’s velocity as it was prior to attachment.
A value of
1
corresponds to the total animation distance traversed in one second. For example, if the total animation distance is 200 points and you want the start of the animation to match a view velocity of 100 pt/s, use a value of0.5
.
So, 500
means that you want to travel 500 times the distance between the current location and the final location in one second. That's absurdly fast.
I would try to match the initial velocity with the final velocity of gesture. So, calculate the absolute velocity of the gesture, and divide that by the distance to be traveled divided by the duration of the animation.
Third, if you're dragging it off screen, I'm not sure why you're using a usingSpringWithDamping
of 0.5
. That's springy. And why would you want it to spring at all you're using as it stops off screen and you can't see it anyway? It strikes me that any value less than 1.0
risks the possibility of the view springing back into the visible screen during one or more of the initial springs. I wouldn't use anything less than 1.0
here if animating off screen.
Pulling that all together, I get something like:
CGPoint velocityOfPan = [pan velocityInView:self.view];
CGFloat velocityOfPanAbsolute = hypof(velocityOfPan.x, velocityOfPan.y); // get simple points per second
CGPoint currentPoint = _view2.center.x;
CGPoint finalPoint = CGPointMake(_view2.center.x, -600);
CGFloat distance = hypof(finalPoint.x - currentPoint.x, finalPoint.y - currentPoint.y); // how far to travel
CGFloat duration = 0.5;
CGFloat animationVelocity = velocityOfPanAbsolute / (distance / duration);
[UIView animateWithDuration:duration delay:0.0 usingSpringWithDamping:1.0 initialSpringVelocity:animationVelocity options:0 animations:^{
_view2.center = CGPointMake(_view2.center.x, -600);
} completion:nil];
Having said all of that, while it's possible animateWithDuration
is getting confused by the combination of "ease in-ease out" and the dramatic initialSpringVelocity
, I don't actually see why that would cause a "hiccup".
Are you doing anything else immediately after this animation? Anything that might be blocking the main queue?
Do you mean the little delay before the gesture is first recognized? Yes, that's a feature of pan gesture recognizer. (I think it's trying to determine whether it's really a pan vs long press vs, etc.).
You can get around this by using a UILongPressGestureRecognizee
with minimumPressDuration
of zero (though you have to calculate translationInView
yourself).
Upvotes: 6