In my code, I have an area on which when users taps and holds, UILongPressGestureRecognizer causes an animation to run and at the end of animation a method gets called.
(It's basically a long tap on a small area square which contains one CGPoint so that it starts a long press animation and when it finishes, the point is removed.)
The problem: On iPhone 5s,SE and 6, UILongPressGestureRecognizer works fine but on any device released after that, it gets dismissed very quickly and I have tried it on at least 11 different devices.
Why is that?!
I appreciate any help.
-(void)prepareLongPressGesture{
_longGesture = [[UILongPressGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleLongGesture:)];
_longGesture.delegate = self;
_longGesture.allowableMovement = self.frame.size.width*0.15;
_longGesture.minimumPressDuration = 1.0;
[self addGestureRecognizer:_longGesture];
}
- (void)handleLongGesture:(UILongPressGestureRecognizer*)gesture {
switch (gesture.state) {
case UIGestureRecognizerStateBegan:
[self removeModifiablePoint];
break;
case UIGestureRecognizerStateChanged:
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled: {
if (self.animatedView.superview) {
[self.animatedView removeFromSuperview];
}
} break;
default:
break;
}
}
- (void)removeModifiablePoint {
CGFloat sizeOFLongPressAnimation = 0.0;
if (IS_IPAD) {
sizeOFLongPressAnimation = 0.15*self.frame.size.width;
}else{
sizeOFLongPressAnimation = 0.27*self.frame.size.width;
}
if (!_animatedView) {
self.animatedView =
[[UIView alloc] initWithFrame:CGRectMake(0, 0, sizeOFLongPressAnimation, sizeOFLongPressAnimation)];
self.animatedView.alpha = 0.7f;
self.animatedView.layer.borderColor = [UIColor redColor].CGColor;
self.animatedView.layer.borderWidth = 3.f;
self.animatedView.layer.cornerRadius = sizeOFLongPressAnimation / 2.0f;
self.progressView = [[UIView alloc] initWithFrame:self.animatedView.bounds];
self.progressView.backgroundColor = [UIColor redColor];
self.progressView.layer.cornerRadius = sizeOFLongPressAnimation / 2.0f;
[self.animatedView addSubview:self.progressView];
}
CGPoint center = [self.modifiablePointsArray[_modifiablePointIndex] CGPointValue];
self.animatedView.center = center;
[self addSubview:self.animatedView];
self.progressView.transform =
CGAffineTransformScale(CGAffineTransformIdentity, 0.1, 0.1f);
[UIView animateWithDuration:2.f animations:^{
self.progressView.transform = CGAffineTransformIdentity;
}completion:^(BOOL finished) {
if (finished) {
[self.animatedView removeFromSuperview];
[self.modifiablePointsArray removeObjectAtIndex:_modifiablePointIndex];
[self setNeedsDisplay];
}
}];
}
Related
I've got a UICollectionView with horizontal scrolling. Each cell corresponds to a object (a currency code) in an array. What I would like is to be able to reorder cells via a drag and drop gesture.
I found a tutorial for UITableView and tried it but when I hold and drag a cell it only moves vertically and doesn't scroll when I move my finger to the edge of the screen. Here is a gif.
What I want to happen is for the cell to move horizontally, instead of vertically, and for the collection view to scroll when the edge of the screen is reached. How would I achieve this?
This is what I have now:
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget: self action: #selector(longPressGestureRecognised:)];
[self.collectionView addGestureRecognizer: longPress];
-(IBAction) longPressGestureRecognised:(id)sender
{
UILongPressGestureRecognizer *longPress = (UILongPressGestureRecognizer *)sender;
UIGestureRecognizerState state = longPress.state;
CGPoint location = [longPress locationInView: self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint: location];
static UIView *snapshot = nil;
static NSIndexPath *sourceIndexPath = nil;
switch (state) {
case UIGestureRecognizerStateBegan: {
if (indexPath) {
sourceIndexPath = indexPath;
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath: indexPath];
// Take a snapshot of the selected item using helper method.
snapshot = [self customSnapshotFromView: cell];
// Add the snapshot as subview, centered at cell's centre.
__block CGPoint centre = cell.center;
snapshot.center = centre;
snapshot.alpha = 0.0;
[self.collectionView addSubview: snapshot];
[UIView animateWithDuration: 0.25 animations:^{
// Offset for gesture location.
centre.y = location.y;
snapshot.center = centre;
snapshot.transform = CGAffineTransformMakeScale(1.05, 1.05);
snapshot.alpha = 0.98;
// Fade out.
cell.alpha = 0.0;
} completion: ^(BOOL finished) {
cell.hidden = YES;
}];
}
break;
}
case UIGestureRecognizerStateChanged: {
CGPoint centre = snapshot.center;
centre.y = location.y;
snapshot.center = centre;
// Is destination valid and is it different from source?
if (indexPath && ![indexPath isEqual: sourceIndexPath]) {
// Update data source.
[currencyArray exchangeObjectAtIndex: indexPath.item withObjectAtIndex:sourceIndexPath.item];
// Move the items.
[self.collectionView moveItemAtIndexPath: sourceIndexPath toIndexPath: indexPath];
// And update source so it is in sync with UI changes.
sourceIndexPath = indexPath;
}
break;
}
default: {
// Clean up.
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath: sourceIndexPath];
cell.hidden = NO;
cell.alpha = 0.0;
[UIView animateWithDuration: 0.25 animations: ^{
snapshot.center = cell.center;
snapshot.transform = CGAffineTransformIdentity;
snapshot.alpha = 0.0;
// Undo fade out.
cell.alpha = 1.0;
}completion: ^(BOOL finished) {
sourceIndexPath = nil;
[snapshot removeFromSuperview];
snapshot = nil;
}];
break;
}
}
}
-(UIView *) customSnapshotFromView:(UIView *)inputView
{
// Make an image from the input view.
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, NO, 0);
[inputView.layer renderInContext: UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Create an image view.
UIView *snapshot = [[UIImageView alloc] initWithImage: image];
snapshot.layer.masksToBounds = NO;
snapshot.layer.cornerRadius = 0.0;
snapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0);
snapshot.layer.shadowRadius = 5.0;
snapshot.layer.shadowOpacity = 0.4;
return snapshot;
}
EDIT:
I've figured it out. Here is the code:
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget: self action: #selector(handleLongGesture:)];
[self.collectionView addGestureRecognizer: longPress];
-(IBAction) handleLongGesture: (id)sender {
UILongPressGestureRecognizer *longPress = (UILongPressGestureRecognizer *)sender;
CGPoint location = [longPress locationInView: self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint: location];
switch (longPress.state) {
case UIGestureRecognizerStateBegan:
[self.collectionView beginInteractiveMovementForItemAtIndexPath: indexPath];
NSLog(#"Gesture began");
break;
case UIGestureRecognizerStateChanged:
[self.collectionView updateInteractiveMovementTargetPosition: [longPress locationInView: longPress.view]];
NSLog(#"Gesture state changed");
break;
case UIGestureRecognizerStateEnded:
[self.collectionView endInteractiveMovement];
NSLog(#"Gesture state ended");
break;
default:
[self.collectionView cancelInteractiveMovement];
NSLog(#"Gesture cancelled");
break;
}
}
in UIGestureRecognizerStateChanged:
change this line of code
CGPoint centre = snapshot.center;
centre.x = location.x;
snapshot.center = centre;
centre.y returns y axis dragging
change it to centre.x for x axis dragging
I am in charge to update an application in IOS8. But I have a problem when I want to switch between two view (the old and the new). The background of the UIWindow's Application jumped. It appears during 1 second and disappear. I can not find the issue. I tap on a backButton to change the content view.
- (void)onBackTap
{
NSLog(#"Standard back");
[self goBackWithAnimation:AnimatePushFromLeft];
}
- (void)goBackWithAnimation:(GraphNavigationAnimation)animation
{
NSLog(#"History Before ---> %#", [self currentHistory]);
[[self currentHistory] removeLastObject];
NSLog(#"History After ---> %#", [self currentHistory]);
self.currentVC = [[self currentHistory] lastObject];
[view_ switchContentViewTo:currentVC_.view
navigationItemStack:[[self currentHistory] valueForKey:#"navigationItem"]
isNavbarVisible:navbarVisible_
animation:animation];
}
Thank you for your support if you have already found a solution.
This is my procedure to switch from an old and a new contentView:
-(void)switchContentViewTo:(UIView *)newView navigationItemStack:(NSArray*)navigationItemStack isNavbarVisible:(BOOL)isNavbarVisible animation:GraphNavigationAnimation)animation {
BOOL isPrevNavbarVisible = navbarVisible_;
navbarVisible_ = isNavbarVisible;
if (!contentView_) {
contentView_ = newView;
[self insertSubview:contentView_ belowSubview:navbar_];
[navbar_ setItems:navigationItemStack animated:NO];
[self setNeedsLayout];
return;
}
if (newView == contentView_) {
[navbar_ setItems:navigationItemStack animated:NO];
return;
}
CGRect const bigFrame = self.bounds;
CGRect const smallFrame = CGRectMake(0, navbarHeight_,
self.bounds.size.width, self.bounds.size.height - navbarHeight_);
CGRect centerFrame = isNavbarVisible
? smallFrame
: bigFrame;
void (^step1)(void) = ^{
CGRect newViewStartFrame;
switch (animation) {
case AnimatePushFromRight: {
newViewStartFrame = CGRectOffset(centerFrame, contentView_.bounds.size.width, 0);
break;
}
case AnimatePushFromLeft: {
newViewStartFrame = CGRectOffset(centerFrame, -contentView_.bounds.size.width, 0);
break;
}
default:
newViewStartFrame = centerFrame;
break;
}
navbar_.hidden = !isNavbarVisible && !isPrevNavbarVisible;
if (isNavbarVisible && !isPrevNavbarVisible) {
navbar_.frame = CGRectMake(newViewStartFrame.origin.x, 0,
newViewStartFrame.size.width, navbarHeight_);
}
newView.frame = newViewStartFrame;
[self insertSubview:newView belowSubview:navbar_];
self.userInteractionEnabled = NO;
animating_ = YES;
};
void (^step2)(void) = ^{
CGRect oldViewFinishFrame;
switch (animation) {
case AnimatePushFromRight: {
oldViewFinishFrame = CGRectOffset(
contentView_.frame, -contentView_.bounds.size.width, 0);
break;
}
case AnimatePushFromLeft: {
oldViewFinishFrame = CGRectOffset(
contentView_.frame, contentView_.bounds.size.width, 0);
break;
}
default:
oldViewFinishFrame = contentView_.frame;
break;
}
if (!isNavbarVisible && isPrevNavbarVisible) {
navbar_.frame = CGRectMake(oldViewFinishFrame.origin.x, 0,
oldViewFinishFrame.size.width, navbarHeight_);
} else {
navbar_.frame = CGRectMake(centerFrame.origin.x, 0,
centerFrame.size.width, navbarHeight_);
}
newView.frame = centerFrame;
contentView_.frame = oldViewFinishFrame;
};
void (^step3)(void) = ^{
[contentView_ removeFromSuperview];
contentView_ = newView;
self.userInteractionEnabled = YES;
animating_ = NO;
};
[navbar_ setItems:navigationItemStack animated:(animation != AnimateNone)];
step1();
[UIView animateWithDuration:(animation != AnimateNone ? animationDuration : 0)
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
step2();
}
completion:^(BOOL finished){
step3();
}];
}
In my application I have a view that sits on top of another view. The user is able to swipe the top view to the right, which fades it out and "fades in" the back view.
When the user pans to the right and lets go, the animation kicks in and it should translate from its current position to the new off screen position. The problem is that the animation instead snaps the view back to its starting point and then does the animation.
Here is my code for the pan gesture and animation:
- (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer
{
CGPoint translation = [recognizer translationInView:recognizer.view];
CGPoint velocity = [recognizer velocityInView:recognizer.view];
switch (recognizer.state) {
case UIGestureRecognizerStateBegan: {
[recognizer setTranslation:CGPointMake(self.slidingView.frame.origin.x, 0) inView:recognizer.view];
break;
}
case UIGestureRecognizerStateChanged: {
[self.slidingView setTransform:CGAffineTransformMakeTranslation(MAX(0,translation.x), 0)];
CGFloat percentage = fmaxf(0,(translation.x/recognizer.view.bounds.size.width));
[self.backgroundBlurImageView setAlpha:1-percentage];
[self.slidingView setAlpha:1-percentage];
[self.backgroundImageView setTransform:CGAffineTransformMakeScale(1 + (percentage * 0.05), 1 + (percentage * 0.05))];
[self.backgroundBlurImageView setTransform:CGAffineTransformMakeScale(1 + (percentage * 0.05), 1 + (percentage * 0.05))];
break;
}
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled: {
if (velocity.x > 5.0 || (velocity.x >= -1.0 && translation.x > kMenuViewControllerMinimumPanDistanceToOpen * self.slidingView.bounds.size.width)) {
CGFloat transformedVelocity = velocity.x/ABS(self.slidingView.bounds.size.width - translation.x);
CGFloat duration = 0.66;
[self showViewAnimated:YES duration:duration initialVelocity:transformedVelocity];
} else {
[self hideViewAnimated:YES];
}
}
default:
break;
}
}
- (void)showViewAnimated:(BOOL)animated duration:(CGFloat)duration
initialVelocity:(CGFloat)velocity;
{
// animate
__weak typeof(self) blockSelf = self;
[UIView animateWithDuration:animated ? duration : 0.0 delay:0
usingSpringWithDamping:0.8f initialSpringVelocity:velocity options:UIViewAnimationOptionAllowUserInteraction animations:^{
blockSelf.slidingView.transform = CGAffineTransformMakeTranslation(blockSelf.slidingView.bounds.size.width, 0);
[blockSelf.backgroundBlurImageView setTransform:CGAffineTransformMakeScale(1.05, 1.05)];
[blockSelf.backgroundImageView setTransform:CGAffineTransformMakeScale(1.05, 1.05)];
[blockSelf.backgroundBlurImageView setAlpha:0];
[blockSelf.slidingView setAlpha:0];
} completion:^(BOOL finished) {
blockSelf.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTapOnAaron:)];
[blockSelf.view addGestureRecognizer:self.tapGestureRecognizer];
}];
}
- (void)hideViewAnimated:(BOOL)animated;
{
__weak typeof(self) blockSelf = self;
[UIView animateWithDuration:0.3f animations:^{
blockSelf.slidingView.transform = CGAffineTransformIdentity;
[blockSelf.backgroundBlurImageView setTransform:CGAffineTransformIdentity];
[blockSelf.backgroundImageView setTransform:CGAffineTransformIdentity];
[blockSelf.backgroundBlurImageView setAlpha:1];
[blockSelf.slidingView setAlpha:1];
} completion:^(BOOL finished) {
[blockSelf.view removeGestureRecognizer:self.tapGestureRecognizer];
blockSelf.tapGestureRecognizer = nil;
}];
}
I have already tried settings the animation options to:
UIViewAnimationOptionBeginFromCurrentState
with no effect.
Anyone have a clue what I am missing? Thanks
Kyle, sometimes this sort of thing happens if you are using autolayout. If you don't need it try disabling it and see if it fixes it.
I want to add an UIImageView to my view at the users touch location and have the UIImageView grow while the user is holding their finger down. Think of a ballon being blown up. I want the center of the UIImageView to remain at the user's touch location while its growing.
I figured the best way would be a UILongPressGestureRecognizer and wrote the below. This does work as I planed except the visual effect is somewhat choppy and clumsy.
Is there any way that I can animate the UIImageView's size until the UILongPressGestureRecognizer calls UIGestureRecognizerStateEnded?
Or, is there a better way to do this altogether?
declared in .h: CGPoint longPressLocation;
.m:
- (IBAction) handleInflation:(UILongPressGestureRecognizer *) inflateGesture {
longPressLocation= [inflateGesture locationInView:self.view];
switch (inflateGesture.state) {
case UIGestureRecognizerStateBegan:{
NSLog(#"Long press Began .................");
inflateTimer = [NSTimer scheduledTimerWithTimeInterval:.4 target:self selector:#selector(inflate) userInfo:nil repeats:YES];
UIImage *tempImage=[UIImage imageNamed:#"bomb.png"];
UIImageView *inflatableImageView = [[UIImageView alloc] initWithFrame:CGRectMake(longPressLocation.x-tempImage.size.width/2,
longPressLocation.y-tempImage.size.height/2,
tempImage.size.width, tempImage.size.height)];
inflatableImageView.image = tempImage;
[bonusGame addSubview:inflatableImageView];
inflatable=inflatableImageView;
}
break;
case UIGestureRecognizerStateChanged:{
NSLog(#"Long press Changed .................");
}
break;
case UIGestureRecognizerStateEnded:{
NSLog(#"Long press Ended .................");
[inflateTimer invalidate];
}
break;
default:
break;
}
}
-(void)inflate{
inflatable.frame=CGRectMake(inflatable.frame.origin.x,inflatable.frame.origin.y , inflatable.bounds.size.width+15, inflatable.bounds.size.height+15);
inflatable.center=longPressLocation;
}
Final Working Code:
- (IBAction) handleInflation:(UILongPressGestureRecognizer *) inflateGesture {
inflateGesture.minimumPressDuration = .01;
longPressLocation= [inflateGesture locationInView:self.view];
switch (inflateGesture.state) {
case UIGestureRecognizerStateBegan:{
NSLog(#"Long press Began .................");
inflateStart = [NSDate date];
inflateDisplayLink = [NSClassFromString(#"CADisplayLink") displayLinkWithTarget:self selector:#selector(inflate)];
[inflateDisplayLink setFrameInterval:1];
[inflateDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
UIImage *tempImage=[UIImage imageNamed:#"bomb.png"];
UIImageView *inflatableImageView = [[UIImageView alloc] initWithFrame:CGRectMake(longPressLocation.x-tempImage.size.width/2,
longPressLocation.y-tempImage.size.height/2,
tempImage.size.width, tempImage.size.height)];
inflatableImageView.image = tempImage;
[bonusGame addSubview:inflatableImageView];
inflatable=inflatableImageView;
}
break;
case UIGestureRecognizerStateChanged:{
NSLog(#"Long press Changed .................");
}
break;
case UIGestureRecognizerStateEnded:{
NSLog(#"Long press Ended .................");
[inflateDisplayLink invalidate];
}
break;
default:
break;
}
}
-(void)inflate{
NSDate *inflateEnd = [NSDate date];
NSTimeInterval inflateInterval;
inflateInterval = ([inflateEnd timeIntervalSince1970] - [inflateStart timeIntervalSince1970])*25;
inflatable.frame=CGRectMake(inflatable.frame.origin.x,
inflatable.frame.origin.y ,
inflatable.bounds.size.width+inflateInterval,
inflatable.bounds.size.height+inflateInterval);
inflatable.center=longPressLocation;
if(inflatable.bounds.size.width>200){
[inflateDisplayLink invalidate];
}
}
A timer may not be smooth. Instead check out CADisplayLink, which will provide a delegate callback whenever the device's screen is redrawn (~60hz), so you will get a per frame chance to adjust your balloon size.
Another thing to consider is the time between refreshes isn't constant, it could be a lot slower than when the last refresh occured, so if you are incrementing the size by a constant 15 every time you get a callback, then the animation may not seem smooth.
To combat this, when you start the animation take a timestamp and hold onto it, then when you inflate the balloon take another timestamp and determine the difference between now and the last redraw, then multiply the difference by some value, which will ensure a constant smooth size growth - this is called a timestep.
https://developer.apple.com/library/ios/documentation/QuartzCore/Reference/CADisplayLink_ClassRef/Reference/Reference.html
Getting creative here, but I think this might work:
You start an animation that animates the images frame from its current size towards a maximum size.
Start the animation as soon as you detect the long press gesture.
If the gesture ends, get the current frame size from the presentationLayer of the animated view/image and update the view/image's frame to that size by starting a new animation with UIViewAnimationOptionBeginFromCurrentState so that the old animation will stop.
That should give you a smoothly growing balloon.
Try this....
imageView = [[UIImageView alloc] initWithFrame:(CGRectMake(120, 100, 30, 30))];
imageView.backgroundColor = [UIColor redColor];
[self.view addSubview:imageView];
imageView.userInteractionEnabled = TRUE;
UILongPressGestureRecognizer *g = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[g setMinimumPressDuration:0.001];
[imageView addGestureRecognizer:g];
And Add these methods
-(void)longPress:(UITapGestureRecognizer *)t
{
if (t.state == UIGestureRecognizerStateBegan)
{
[timer invalidate];
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(makeIncrc) userInfo:nil repeats:TRUE];
}
else if (t.state == UIGestureRecognizerStateEnded || t.state == UIGestureRecognizerStateCancelled)
{
[timer invalidate];
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(makeDec) userInfo:nil repeats:TRUE];
}
}
-(void)makeIncrc
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:(UIViewAnimationCurveLinear)];
CGRect frame = imageView.frame;
frame.origin.x = frame.origin.x - 5;
frame.origin.y = frame.origin.y - 5;
frame.size.width = frame.size.width + 10;
frame.size.height = frame.size.height + 10;
imageView.frame = frame;
[UIView commitAnimations];
}
-(void)makeDec
{
if (imageView.frame.size.width < 20 || imageView.frame.size.height < 20)
{
[timer invalidate];
}
else
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:(UIViewAnimationCurveLinear)];
CGRect frame = imageView.frame;
frame.origin.x = frame.origin.x + 5;
frame.origin.y = frame.origin.y + 5;
frame.size.width = frame.size.width - 10;
frame.size.height = frame.size.height - 10;
imageView.frame = frame;
[UIView commitAnimations];
}
}
Here's a Swift version. I had to replace invalidate() calls with "paused" in order to avoid runaway inflation.
var inflateDisplayLink: CADisplayLink?
var inflateStart: NSDate!
var longPressLocation: CGPoint!
var inflatable: UIImageView!
func handleInflation(inflateGesture: UILongPressGestureRecognizer) {
longPressLocation = inflateGesture.locationInView(self.view)
let imageView = inflateGesture.view as! UIImageView
switch (inflateGesture.state) {
case .Began:
println("Long press Began .................")
inflateStart = NSDate()
let tempImageView = UIImageView(image: imageView.image)
tempImageView.contentMode = .ScaleAspectFit
tempImageView.frame = imageView.frame
self.view.addSubview(tempImageView)
inflatable = tempImageView
if inflateDisplayLink == nil {
inflateDisplayLink = CADisplayLink(target: self, selector: Selector("inflate"))
inflateDisplayLink!.frameInterval = 1
inflateDisplayLink!.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
} else {
inflateDisplayLink!.paused = false
}
break
case .Changed:
println("Long press Changed .................")
break
case .Ended:
println("Long press Ended .................")
inflateDisplayLink!.paused = true
break
default:
break
}
}
func inflate() {
var inflateEnd = NSDate()
// Convert from Double to CGFloat, due to bug (?) on iPhone5
var inflateInterval: CGFloat = CGFloat((Double(inflateEnd.timeIntervalSince1970) - Double(inflateStart.timeIntervalSince1970))) * 5.0
inflatable.frame = CGRectMake(inflatable.frame.origin.x, inflatable.frame.origin.y, inflatable.bounds.size.width + inflateInterval, inflatable.bounds.size.height + inflateInterval)
inflatable.center = longPressLocation.center
if inflatable.bounds.size.width > 200 {
inflateDisplayLink!.paused = true
}
}
I am trying to animate the dealing of cards. Currently I have two cards, the dealer card (dCard1) and the player card (pCard1).
The player card should be dealt and as soon as it is dealt, the dealer card should then be dealt.
What is currently happening is the player card will animate properly when I press the deal button, but instead of waiting till the player card is done animating, the dealer card just appears at the dealerTo location. It does not animate. I am new to doing animations any help would be greatly appreciated.
-(void) animateHandsDealt: (Hand*) pHand:(Hand*) dHand{
pCard1= [[UIImageView alloc] initWithFrame:CGRectMake(125, 0, 75, 110)];
dCard1= [[UIImageView alloc] initWithFrame:CGRectMake(125, 0, 75, 110)];
CGFloat start = 0;
CGFloat duration = 1;
CGPoint playerTo = CGPointMake(120, 300);
CGPoint dealerTo = CGPointMake(120, 70);
pCard1.image = [UIImage imageNamed:[[pHand.cardArr objectAtIndex:0 ] imagePath]];
[self.view addSubview: pCard1];
[playerImages addObject:pCard1];
CABasicAnimation *move = [CABasicAnimation animationWithKeyPath:#"position" ];
move.fillMode = kCAFillModeBoth;
move.beginTime = start;
[move setToValue:[NSValue valueWithCGPoint:playerTo]];
[move setDuration:duration];
move.removedOnCompletion = FALSE;
[[pCard1 layer] addAnimation:move forKey:#"position"];
dCard1.image = [UIImage imageNamed:#"back-red-75-1.png"];
[self.view addSubview: dCard1];
CABasicAnimation *move2 = [CABasicAnimation animationWithKeyPath:#"position" ];
[move2 setToValue:[NSValue valueWithCGPoint:dealerTo]];
move2.beginTime = start + duration;
[move2 setDuration:duration];
move2.fillMode = kCAFillModeBoth;
move2.removedOnCompletion = FALSE;
[[dCard1 layer] addAnimation:move2 forKey:#"position"];
}
If you're just animating the frame of the view, you can use the much simpler UIView animateWithDuration... methods.
You chain them by using the animateWithDuration:animations:completion: version. You can specify the second animation in the completion: block of the first animation.
http://developer.apple.com/library/ios/#documentation/uikit/reference/uiview_class/UIView/UIView.html#//apple_ref/doc/uid/TP40006816-CH3-SW109
I don't have access to your classes (of course), but I think this is pretty close to "drop-in-ready" code. What do you think?
- (void)deal {
// Of course, you'd replace these NSString objects with actual card objects...
NSArray *array = [NSArray arrayWithObjects:#"pCard1", #"dCard1", #"pCard2", #"dCard2", #"pCard3", #"dCard3", nil];
[self performSelectorOnMainThread:#selector(animateDealWithArray:) withObject:array waitUntilDone:YES];
}
- (void)animateDealWithArray:(NSArray *)array {
// constants that won't change call to call
static CGFloat duration = 1;
static CGSize cardSize = {75, 110};
static CGPoint playerTo = {120, 300};
static CGPoint dealerTo = {120, 70};
// Seems like you want the card to start at the top of the "deck" whether it's a player card or not
UIImageView *card = [[UIImageView alloc] initWithFrame:CGRectMake(125, 0, 75, 110)];
// Figure out if the first object (the one we're currently using) is a player or dealer card
if ([[array objectAtIndex:0] isKindOfClass:[PlayerCard Class]]) {
// Player card, so show the "face"
[card setImage:[UIImage imageNamed:[(PlayerCard *)[array objectAtIndex:0] imagePath]]];
[UIView animateWithDuration:duration
animations:^{
[card setFrame:CGRectMake(playerTo.x, playerTo.y, cardSize.width, cardSize.height)];
}
completion:^(BOOL finished) {
[self dealNext:array];
}
];
} else {
// Dealer card, so show the back
[card setImage:[UIImage imageNamed:#"back-red-75-1.png"]];
[UIView animateWithDuration:duration
animations:^{
[card setFrame:CGRectMake(dealerTo.x, dealerTo.y, cardSize.width, cardSize.height)];
}
completion:^(BOOL finished) {
[self dealNext:array];
}
];
}
}
- (void)dealNext:(NSArray *)array {
int count = [array count];
if (count > 1) {
NSArray *newArray = [array subarrayWithRange:NSMakeRange(1, count - 1)];
[self animateDealWithArray:newArray];
}
}