Objective C animates wrong button when scrolling in tablview - ios

- (IBAction)playShout:(UIButton *)sender {
self.tableView.userInteractionEnabled=NO;
CGPoint point = [sender.superview convertPoint:sender.frame.origin toView:self.tableView];
NSIndexPath *indexPath = self.tempIndexPath;//[self.tableView indexPathForRowAtPoint:point];
DisplayPersonTableViewCell * cell=[self.tableView cellForRowAtIndexPath:indexPath];
UIButton * button=cell.playButton;
self.tableView.userInteractionEnabled=YES;
PFObject * shoutToPlay=[self.shoutsArrayForUserBeingDisplayed objectAtIndex:indexPath.row];
[shoutToPlay fetchIfNeeded];
[AnimationViewController endCircleAnimation];
NSData * audioData=[self.audioDataDict objectForKey:shoutToPlay.objectId];
if (player.playing) {
if(player.data==audioData) {
[player stop];
[AnimationViewController endCircleAnimation];
} else{
[self setAllTableViewCellsToPlayButton];
[AnimationViewController endCircleAnimation];
sender.layer.cornerRadius=sender.frame.size.width/2;
player = [[AVAudioPlayer alloc] initWithData:audioData error:nil];
player.delegate=self;
[player prepareToPlay];
if (DISPLAY_IS_MUTED) {
player.volume=0;
}
[player play];
[AnimationViewController animateButton:cell.playButton ForDuration:player.duration withColor:[UIColor whiteColor]];
}
} else {
sender.layer.cornerRadius=sender.frame.size.width/2;
player = [[AVAudioPlayer alloc] initWithData:audioData error:nil];
player.delegate=self;
[player prepareToPlay];
if (DISPLAY_IS_MUTED) {
player.volume=0;
}
[player play];
[AnimationViewController animateButton:sender ForDuration:player.duration withColor:[UIColor whiteColor]];
}
}
Very simple and very easy, of course. But if I perform an animation on a button in that cell, and then scroll the tableview, it will switch and start animating the wrong cells. It's as if the fact that the superview's frame is changing affect's which button is being animated.
Any advice?
Animation method:
+(void)animateButton:(UIButton *)button ForDuration:(NSTimeInterval)duration withColor:(UIColor *)color {
circle = [CAShapeLayer layer];
circle.fillColor = nil;
circle.lineWidth = button.frame.size.height/2+2;
circle.strokeColor = color.CGColor;
circle.bounds = CGRectMake(0, 0, button.frame.size.width/2, button.frame.size.height/2);
circle.frame= CGRectMake(button.frame.size.width/4, button.frame.size.height/4, button.frame.size.width/2+2, button.frame.size.height/2+2);
circle.path = [UIBezierPath bezierPathWithOvalInRect:circle.bounds].CGPath;
[button.layer addSublayer:circle];
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = duration;
drawAnimation.repeatCount = 1.0;
drawAnimation.removedOnCompletion = YES;
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawAnimation.toValue = [NSNumber numberWithFloat:1.0f];
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[circle addAnimation:drawAnimation forKey:#"drawCircleAnimation"];
}

Are you implementing the prepareForReuse method in your cell? There you must stop running animations and reset the state of the cell for the reuse.
From the docs:
prepareForReuse
Prepares a reusable cell for reuse by the table view's delegate.
If a UITableViewCell object is reusable—that is, it has a reuse identifier—this method is invoked just before the object is returned
from the UITableView method dequeueReusableCellWithIdentifier:. For
performance reasons, you should only reset attributes of the cell that
are not related to content, for example, alpha, editing, and selection
state. The table view's delegate in tableView:cellForRowAtIndexPath:
should always reset all content when reusing a cell. If the cell
object does not have an associated reuse identifier, this method is
not called. If you override this method, you must be sure to invoke
the superclass implementation.
You need to implement a custom UITableViewCell (create a subclass of it) and implement the prepareForReuse method.
There you can write the logic to stop the animation.

Related

Removing a subview of UICollectionView cell content view crashes if that subview has a animated layer

I have added CABasicAnimation on a layer which is added as sublayer to a custom view.Custom view is then added to collection view cell content view as subview. I want to show indefinite progress like bar moving from left to right until download completes(please refer the attachments). Everything is fine except, once download is completed,For removing the custom view with layer animation, i cannot access that view using the tag. The compiler crashes and throws 'could not load any Objective-C class information. This will significantly reduce the quality of type information available'
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableDictionary *attachDetail = [self getManagedObjectForIndexPath:indexPath];
ZCRMMailAttachmentCollectionViewCell *cell = (ZCRMMailAttachmentCollectionViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:#"cellId" forIndexPath:indexPath];
[self setVisibilityOfRemoveButtonInCell:cell inObj:attachDetail];
[self setVisibilityOfSizeInCell:cell inObj:attachDetail];
cell.attachDetails = attachDetail;
[self addLoadingViewIfNotDownloaded:cell managedObject:attachDetail];
[cell redraw];
return cell;
}
-(void)addLoadingViewIfNotDownloaded:(ZCRMMailAttachmentCollectionViewCell*)cell managedObject:(NSDictionary*)attachDict
{
NSNumber *progress = attachDict[MAIL_ATTACH_DOWNLOAD_PROGRESS];
if(!ZV_IsEmpty(progress))
{
if (isinf([progress floatValue]))
{
[cell addLinearInfiniteProgress];
}
else
{
[cell removeLinearProgress];
}
}
}
-(void)addLinearInfiniteProgress
{
UIView* view = [zTvContentView viewWithTag:1234];
if(!view)
{
UIView *progressView = [UIView new];
progressView.tag = 1234;
progressView.layer.masksToBounds = YES;
[zTvContentView addSubview:progressView];
[progressView setBottom:-1];
[progressView alignTo:VTLayoutAlignLeft|VTLayoutAlignRight];
[progressView setHeight:2];
//ProgressLayer
CGRect frame = self.contentView.bounds;
CALayer * indeterminateLayer = [CALayer layer];
indeterminateLayer.delegate = self;
indeterminateLayer.name = #"progresslayer";
indeterminateLayer.frame = CGRectMake(0, 0, 0.4*CGRectGetWidth(frame), 3);
indeterminateLayer.backgroundColor = [CurTheme() themeActionItemColor].CGColor;
indeterminateLayer.opacity = 1;
[progressView.layer addSublayer:indeterminateLayer];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
animation.duration = 1;
animation.repeatCount = HUGE_VALF;
animation.removedOnCompletion = YES;
animation.fromValue = [NSValue valueWithCGPoint:CGPointMake( CGRectGetMinX(frame) - indeterminateLayer.frame.size.width + OFFSET, 0)];
animation.toValue = [NSValue valueWithCGPoint:CGPointMake(CGRectGetMaxX(frame) + indeterminateLayer.frame.size.width - OFFSET, 0)];
[indeterminateLayer addAnimation:animation forKey:#"position"];
}
}
(void)removeLinearInfiniteProgress
{
UIView* progressView = [zTvContentView viewWithTag:1234];
if(progressView)
{
if ([progressView superview])
{
[progressView removeFromSuperview];
}
}
}
Any help would be appreciated.

Move cells of UICollectionView in iOS?

I have a UICollectionView.I am trying to give it as SpringBoard functionality.I have am able to give the shake animation to each cell.But i want when icons are shaking then i should be able to move them as well.
For shaking the cells i have added the UILongPressGesture on each cell.When gesture end then i have added one custom animation on them & also added a delete button on top left corner.
Code for long press gesture:
declaration of variables
CGPoint p;
UILongPressGestureRecognizer *lpgr;
NSIndexPath *gesture_indexPath;
add gesture to collection view
lpgr
= [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
lpgr.minimumPressDuration = .3; // To detect after how many seconds you want shake the cells
lpgr.delegate = self;
[self.collection_view addGestureRecognizer:lpgr];
lpgr.delaysTouchesBegan = YES;
Callback method
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state != UIGestureRecognizerStateEnded)
{
return;
}
p = [gestureRecognizer locationInView:self.collection_view];
NSIndexPath *indexPath = [self.collection_view indexPathForItemAtPoint:p];
if (indexPath == nil)
{
NSLog(#"couldn't find index path");
}
else
{
[[NSUserDefaults standardUserDefaults]setValue:#"yes" forKey:#"longPressed"];
[self.collection_view reloadData];
}
}
Cell for Item at inde path
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"arr_album index row");
BlogAlbumCell *cell;
static NSString *identifier = #"UserBlogAlbum";
cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UserAlbum *user_allbum=[arr_userAlbums objectAtIndex:indexPath.row];
cell.label_blog_name.text=user_allbum.album_name;
cell.image_blog_image.image = [UIImage imageNamed:#"more.png"];
[cell.image_blog_image setImageWithURL:[NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_allbum.album_image]]];
if([[[NSUserDefaults standardUserDefaults]valueForKey:#"longPressed"] isEqualToString:#"yes"])
{
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
[anim setToValue:[NSNumber numberWithFloat:0.0f]];
[anim setFromValue:[NSNumber numberWithDouble:M_PI/50]];
[anim setDuration:0.1];
[anim setRepeatCount:NSUIntegerMax];
[anim setAutoreverses:YES];
cell.layer.shouldRasterize = YES;
[cell.layer addAnimation:anim forKey:#"SpringboardShake"];
CGFloat delButtonSize = 20;
UIButton *delButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, delButtonSize, delButtonSize)];
delButton.center = CGPointMake(9, 10);
delButton.backgroundColor = [UIColor clearColor];
[delButton setImage: [UIImage imageNamed:#"cross_30.png"] forState:UIControlStateNormal];
[cell addSubview:delButton];
[delButton addTarget:self action:#selector(deleteRecipe:) forControlEvents:UIControlEventTouchUpInside];
}
else if ([[[NSUserDefaults standardUserDefaults]valueForKey:#"singleTap"] isEqualToString:#"yes"])
{
for(UIView *subview in [cell subviews])
{
if([subview isKindOfClass:[UIButton class]])
{
[subview removeFromSuperview];
}
else
{
// Do nothing - not a UIButton or subclass instance
}
}
[cell.layer removeAllAnimations];
// _deleteButton.hidden = YES;
// [_deleteButton removeFromSuperview];
}
return cell;
}
It works fine till here.
For moving the cell i made a sample app in which i added UICollectionViewController & override this method
-(void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
NSLog(#"Move at index path called");
}
This also works fine.It also uses long press gesture & when gesture detetced then i am able to move the cells.But now the issue is at one either i can move cell or animate them.If i add my custom gesture then i am not able to move the images.Please tell me how can i remove this issue?
Take a look at this project called DragDropCollectionView. It implements the dragging and dropping as well as the animation.
Edit: This problem can be broken down into 2 smaller subproblems:
How to animate the cells
How to reorder the cells using the drag and drop.
You should combine these 2 solutions into a subclas of UICollectionView to obtain your main solution.
How to animate the cells
To get the wiggle animation you need add 2 different animation effects:
Move the cells vertically i.e up and down bounce
Rotate the cells
Lastly, add a random interval time for each cell so it does appear that the cells don't animate uniformly
Here is the code:
#interface DragDropCollectionView ()
#property (assign, nonatomic) BOOL isWiggling;
#end
#implementation DragDropCollectionView
//Start and Stop methods for wiggle
- (void) startWiggle {
for (UICollectionViewCell *cell in self.visibleCells) {
[self addWiggleAnimationToCell:cell];
}
self.isWiggling = true;
}
- (void)stopWiggle {
for (UICollectionViewCell *cell in self.visibleCells) {
[cell.layer removeAllAnimations];
}
self.isWiggling = false;
}
//- (UICollectionViewCell *)dequ
- (UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(nonnull NSIndexPath *)indexPath{
UICollectionViewCell *cell = [super dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
if (self.isWiggling) {
[self addWiggleAnimationToCell:cell];
} else {
[cell.layer removeAllAnimations];
}
return [[UICollectionViewCell alloc] init];
}
//Animations
- (void)addWiggleAnimationToCell:(UICollectionViewCell *)cell {
[CATransaction begin];
[CATransaction setDisableActions:false];
[cell.layer addAnimation:[self rotationAnimation] forKey:#"rotation"];
[cell.layer addAnimation:[self bounceAnimation] forKey:#"bounce"];
[CATransaction commit];
}
- (CAKeyframeAnimation *)rotationAnimation {
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"transform.rotation.z"];
CGFloat angle = 0.04;
NSTimeInterval duration = 0.1;
double variance = 0.025;
animation.values = #[#(angle), #(-1 * angle)];
animation.autoreverses = YES;
animation.duration = [self randomizeInterval:duration withVariance: variance];
animation.repeatCount = INFINITY;
return animation;
}
- (CAKeyframeAnimation *)bounceAnimation {
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"transform.translation.y"];
CGFloat bounce = 3.0;
NSTimeInterval duration = 0.12;
double variance = 0.025;
animation.values = #[#(bounce), #(-1 * bounce)];
animation.autoreverses = YES;
animation.duration = [self randomizeInterval:duration withVariance: variance];
animation.repeatCount = INFINITY;
return animation;
}
- (NSTimeInterval)randomizeInterval:(NSTimeInterval)interval withVariance:(double)variance {
double randomDecimal = (arc4random() % 1000 - 500.0) / 500.0;
return interval + variance * randomDecimal;
}
How to reorder the cells using drag and drop
So the idea is this: You’re not moving the actual cell around, but rather moving a UIImageView with a UIImage of the contents of the cell.
The algorithm goes more or less like this. I’ve broken it down into 3 sections, gestureRecognizerBegan, Changed and Ended
gestureRecognizerBegan:
When the gestureRecognizer begins, determine if the long press was indeed on a cell (and not on empty space)
Get a UIImage of the cell (See my method “getRasterizedImageOfCell”)
Hide the cell (i.e alpha = 0), create a UIImageView with the exact frame of the cell so the user does not realize you’ve actually hidden the cell and you’re actually using the imageview.
gestureRecognizerChanged:
Update the center of the UIImageView so that it moves with your finger.
If the user has stopped moving his finge i.e. he is hovering over the cell he wants to replace, you now need to swap the cells. (Look at my function “shouldSwapCells”, this method returns a bool of whether the cells should swap or not)
Move the cell that you were dragging to the new indexPath. (Look at my method “swapDraggedCell”). UICollectionView has a built-in method called "moveItemAtIndexPath: toIndexPath”, I’m not sure if UITableView has the same thing
gestureRecognizerEnd:
“Drop” the UIImageView back onto the cell
change the cell alpha from 0.0 to 1.0 and remove the UIImageView from the view.
Here is the code:
#interface DragDropCollectionView ()
#property (strong, nonatomic) NSIndexPath *draggedCellIndexPath;
#property (strong, nonatomic) UIImageView *draggingImageView;
#property (assign, nonatomic) CGPoint touchOffsetFromCenterOfCell;
#property (strong, nonatomic) UILongPressGestureRecognizer *longPressRecognizer;
#end
#implementation DragDropCollectionView
- (void)handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer {
CGPoint touchLocation = [longPressRecognizer locationInView:self];
switch (longPressRecognizer.state) {
case UIGestureRecognizerStateBegan: {
self.draggedCellIndexPath = [self indexPathForItemAtPoint:touchLocation];
if (self.draggedCellIndexPath != nil) {
UICollectionViewCell *draggedCell = [self cellForItemAtIndexPath:self.draggedCellIndexPath];
self.draggingImageView = [[UIImageView alloc] initWithImage:[self rasterizedImageCopyOfCell:draggedCell]];
self.draggingImageView.center = draggedCell.center;
[self addSubview:self.draggingImageView];
draggedCell.alpha = 0.0;
self.touchOffsetFromCenterOfCell = CGPointMake(draggedCell.center.x - touchLocation.x, draggedCell.center.y - touchLocation.y);
[UIView animateWithDuration:0.4 animations:^{
self.draggingImageView.transform = CGAffineTransformMakeScale(1.3, 1.3);
self.draggingImageView.alpha = 0.8;
}];
}
break;
}
case UIGestureRecognizerStateChanged: {
if (self.draggedCellIndexPath != nil) {
self.draggingImageView.center = CGPointMake(touchLocation.x + self.touchOffsetFromCenterOfCell.x, touchLocation.y + self.touchOffsetFromCenterOfCell.y);
}
float pingInterval = 0.3;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(pingInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSIndexPath *newIndexPath = [self indexPathToSwapCellWithAtPreviousTouchLocation:touchLocation];
if (newIndexPath) {
[self swapDraggedCellWithCellAtIndexPath:newIndexPath];
}
});
break;
}
case UIGestureRecognizerStateEnded: {
if (self.draggedCellIndexPath != nil ) {
UICollectionViewCell *draggedCell = [self cellForItemAtIndexPath:self.draggedCellIndexPath];
[UIView animateWithDuration:0.4 animations:^{
self.draggingImageView.transform = CGAffineTransformIdentity;
self.draggingImageView.alpha = 1.0;
if (draggedCell != nil) {
self.draggingImageView.center = draggedCell.center;
}
} completion:^(BOOL finished) {
[self.draggingImageView removeFromSuperview];
self.draggingImageView = nil;
if (draggedCell != nil) {
draggedCell.alpha = 1.0;
self.draggedCellIndexPath = nil;
}
}];
}
}
default:
break;
}
}
- (UIImage *)rasterizedImageCopyOfCell:(UICollectionViewCell *)cell {
UIGraphicsBeginImageContextWithOptions(cell.bounds.size, false, 0.0);
[cell.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
return image;
}
- (NSIndexPath *)indexPathToSwapCellWithAtPreviousTouchLocation:(CGPoint)previousTouchLocation {
CGPoint currentTouchLocation = [self.longPressRecognizer locationInView:self];
if (!isnan(currentTouchLocation.x) && !isnan(currentTouchLocation.y)) {
if ([self distanceBetweenPoints:currentTouchLocation secondPoint:previousTouchLocation] < 20.0) {
NSIndexPath *newIndexPath = [self indexPathForItemAtPoint:currentTouchLocation];
return newIndexPath;
}
}
return nil;
}
- (CGFloat)distanceBetweenPoints:(CGPoint)firstPoint secondPoint:(CGPoint)secondPoint {
CGFloat xDistance = firstPoint.x - secondPoint.x;
CGFloat yDistance = firstPoint.y - secondPoint.y;
return sqrtf(xDistance * xDistance + yDistance * yDistance);
}
- (void)swapDraggedCellWithCellAtIndexPath:(NSIndexPath *)newIndexPath {
[self moveItemAtIndexPath:self.draggedCellIndexPath toIndexPath:newIndexPath];
UICollectionViewCell *draggedCell = [self cellForItemAtIndexPath:newIndexPath];
draggedCell.alpha = 0.0;
self.draggedCellIndexPath = newIndexPath;
}
Hope this helps :)

View stops animation after navigation

I create my custom loading indicator. So, I update my view like this
#property (nonatomic, readonly) CAShapeLayer *progressLayer;
#property (nonatomic, readwrite) BOOL isAnimating;
- (void)layoutSubviews {
[super layoutSubviews];
self.progressLayer.frame = CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));
[self updatePath];
}
- (void)updatePath {
CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
CGFloat radius = MIN(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2) - self.progressLayer.lineWidth / 2;
CGFloat startAngle = 3 * M_PI_2;
CGFloat endAngle = self.endAngle;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
self.progressLayer.path = path.CGPath;
}
Animation starts here
- (void)startAnimating {
if (self.isAnimating)
return;
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = #"transform.rotation";
animation.duration = 1.0f;
animation.fromValue = #(0.0f);
animation.toValue = #(2 * M_PI);
animation.repeatCount = INFINITY;
[self.progressLayer addAnimation:animation forKey:kLLARingSpinnerAnimationKey];
self.isAnimating = true;
}
- (void)stopAnimating {
if (!self.isAnimating)
return;
[self.progressLayer removeAnimationForKey:kLLARingSpinnerAnimationKey];
self.isAnimating = false;
}
- (CAShapeLayer *)progressLayer {
if (!_progressLayer) {
_progressLayer = [CAShapeLayer layer];
_progressLayer.strokeColor = self.tintColor.CGColor;
_progressLayer.fillColor = nil;
_progressLayer.lineWidth = 1.5f;
}
return _progressLayer;
}
I have a tab bar app, so the problem is when animation start, I click to another tab and then return to first view, my custom indicator doesn't animate. Also this bug(feature) appear in collection view cells if I add my indicator to cells.
I add my indicator by calling simple method addSubview:
[view addSubview:indicator];
[indicator startAnimating];
How can I prevent stop animating after navigation?
Thanks in advance.
Here you go,
//I have used a BOOL viewChange to check if spinner was running or not while user tapped to different view in tabBar.
- (void)viewWillAppear:(BOOL)animated{ //On initial run the viewChange will be false and the condition in if won't run but as spinnerView start animating u could change the viewChange flag to true.
//So when user will be back from second view then this flag would be true and it will start animating.
//As your work is complete then just stop animating and remove from view.
if(viewChange){
[spinnerView stopAnimating];
[spinnerView removeFromSuperview];
spinnerView = nil;
spinnerView = [[LLARingSpinnerView alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/2, 40, 40)];
[self.view addSubview:spinnerView];
[spinnerView startAnimating];
}
}
- (void)viewWillDisappear:(BOOL)animated{
//This is called when user move from current view to another and at that point we user won't be seeing the spinnerView so remove it and release it only if it's animating.
if(spinnerView.isAnimating){
viewChange = YES;
[spinnerView stopAnimating];
[spinnerView removeFromSuperview];
spinnerView = nil;
}
else{
viewChange = NO;
}
}
- (IBAction)stopBtn:(id)sender{ //Spinner is stop animating with tap on button and is removed from the view.
if(spinnerView.isAnimating){
[spinnerView stopAnimating];
[spinnerView removeFromSuperview];
spinnerView = nil; //release the obj of spinnerView.
}
}
- (IBAction)btnTap:(id)sender{ //Spinner is start animating with tap on button
if(!spinnerView)
spinnerView = [[LLARingSpinnerView alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/2, 40, 40)];
spinnerView.lineWidth = 1.5f;
spinnerView.tintColor = [UIColor redColor];
[self.view addSubview:spinnerView];
[spinnerView startAnimating];
}
Hope this helps out and if any doubt do let me know. Also if possible u should avoid user to move to another view when spinnerView is running as it indicates that some sort of service or update is running and at that point user should move to another view till it's finish.

CAKeyframeAnimation not animating until I rotate device

I seem to be missing the obvious when animating a key frame. I have looked at many code samples including Apple's MoveMe, referenced in the CAKeyframeAnimation documentation, yet, I cant find a discrepancy that would cause what I'm seeing.
I create a CGMutablePathRef, then a CAKeyframeAnimation and set it to animate an image view along the path. An animation group is created so I can remove the view when done.
Yet, my animation never shows up. UNTIL I rotate the device. It seems a relayout of the view causes the animation to kickstart. I tried the obvious like [theImageView setNeedsDisplay] or even setNeedsLayout, and on the container view as well. Yet, still cant get it to work when I need to. They only show up when I rotate the device.
In the following, -cgPathFromArray: takes an NSArray of internal opcodes which is converted into a CGPathRef. Its verified to be correct because when I rotate the device, the animation does show along the programmed path.
- (void) animateImage: (NSString*) imageName
onPath: (NSArray*) path
withDuration: (NSString*) duration
{
if (self.sceneView)
{
CGMutablePathRef animationPath = [self cgPathFromArray: path];
if (animationPath)
{
UIImage* image = [self findImage: imageName];
if (image)
{
UIImageView* imageView = [[UIImageView alloc] initWithFrame: CGRectMake(0, 0, image.size.width, image.size.height)];
if (imageView)
{
CAKeyframeAnimation* keyFrameAnimation = [CAKeyframeAnimation animationWithKeyPath: #"position"];
imageView.image = image;
[self.sceneView addSubview: imageView];
keyFrameAnimation.removedOnCompletion = YES;
keyFrameAnimation.fillMode = kCAFillModeForwards;
keyFrameAnimation.duration = duration.floatValue;
keyFrameAnimation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionLinear];
keyFrameAnimation.repeatCount = 0;
keyFrameAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[keyFrameAnimation setPath: animationPath];
//group animation with termination block to cleanup
CAAnimationGroup* group = [CAAnimationGroup animation];
group.duration = keyFrameAnimation.duration;
group.removedOnCompletion = YES;
group.fillMode = kCAFillModeForwards;
group.animations = #[keyFrameAnimation];
CorpsAnimationCompletionBlock theBlock = ^void(void)
{
[imageView removeFromSuperview];
};
[group setValue: theBlock
forKey: kCorpsAnimationCompletionBlock];
group.delegate = self;
[imageView.layer addAnimation: group
forKey: nil];
}
}
}
}
}
Anyone can help with this?
You are probably having trouble because you're adding an animation to a layer in the same transaction where the layer is added to the visible layer tree. Core Animation doesn't like to attach animations to layers that haven't been committed yet. You may be able to work around this by doing [CATransaction flush] after adding the layer.
Your code is rather hard to look at because of the excessive nesting. Consider using early returns to make it more readable.
Also, you're explicitly creating the same frame that the -[UIImage initWithImage:] initializer would create for you.
If you're using an animation group and setting a delegate simply so you can execute a block at the end of the animation, there is an easier way. You can begin a CATransaction, set the transaction's completion block, then add the animation, then commit the transaction.
Thus:
- (void) animateImage:(NSString *)imageName onPath: (NSArray *)path
withDuration: (NSString *)duration
{
if (!self.sceneView)
return;
CGMutablePathRef animationPath = [self cgPathFromArray:path];
if (!animationPath)
return;
UIImage *image = [self findImage:imageName];
if (!image)
return;
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self.sceneView addSubview: imageView];
// commit the implicit transaction so we can add an animation to imageView.
[CATransaction flush];
[CATransaction begin]; {
[CATransaction setCompletionBlock:^{
[imageView removeFromSuperview];
}];
CAKeyframeAnimation *animation = [CAKeyframeAnimation
animationWithKeyPath:#"position"];
animation.duration = duration.floatValue;
animation.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionLinear];
animation.path = animationPath;
[imageView.layer addAnimation:animation forKey:animation.keyPath];
} [CATransaction commit];
}

After integrating the custom Table, how to take the lable view from that constructed table?

I'm trying to animate a UITableView and running into difficulties. I have loaded the table using custom cells, and I need to get that table's elements again, and move the content of that row downwards in an animation.
While I perform this, I can get only the entire table from the UIView. Actually I want to get the custom table cell content from the table and perform the animation on that.
I'm struggling with:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UILabel *temp=[[UILabel alloc]init];
temp.text=nil;
UIView *anim = nil;
id currentObject;
for (UIView *theview in destination.superview.subviews)
{
if ([theview isKindOfClass:[UITableView class]])
{
NSLog(#"%#",theview);
UIView *vi= theview;//(UITableView *)[UITableView class];
NSLog(#"%#",vi);
for(UIView *vi1 in vi.superview.subviews )
{
if ([vi1 isKindOfClass:[UITableViewCell class]])
{
NSLog(#"%#",vi1);
}
anim = theview;
break;
}
}
}
How do I get the data from that?
now i am just created the custom UILable and assigned my cell text to it and then animated that....
here is the code...
in the table viewDidSelect method i coded this....
NSArray *visibleCells = [tableView visibleCells];
NSLog(#"the count %d",[visibleCells count]);
//UITableViewCell *aCell=[tableView cellForRowAtIndexPath:indexPath];
UITableViewCell *visiblecell=[tableView cellForRowAtIndexPath:indexPath];
NSLog(#"the count %#",aCell);
NSLog(#"The visible cell ::%#",visiblecell);
int i=0;
for (UITableViewCell *theCell in visibleCells)
{
if ([theCell isEqual:visiblecell])
{
selectedView.frame =CGRectMake( 0,i*(480/[visibleCells count]),320,50);
break;
}
i++;
}
anim=selectedView;
if (!anim)
return;
[self pathGenerate:anim];
and i having the separate animate function just i called that...
- (void)pathGenerate:(UIView *)anim
{
// [self.tabBarController.tabBar insertSubview:(UIImageView*)[UIImage imageNamed:#"Coffee_smoke.png"] atIndex:0];
self.tabBarItem.image=nil;
UIBezierPath *movePath = [UIBezierPath bezierPath];
[movePath moveToPoint:anim.center];//-->down---->destinationPoint.center
[movePath addQuadCurveToPoint:CGPointMake(170, 420)
controlPoint:CGPointMake(200,10)];/* // 2nd phase //destinationPoint.center.x+200, destinationPoint.center.y-100)];*/
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = YES;
CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:#"transform"];
scaleAnim.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
scaleAnim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];
scaleAnim.removedOnCompletion = YES;
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:#"alpha"];
opacityAnim.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnim.toValue = [NSNumber numberWithFloat:0.1];
opacityAnim.removedOnCompletion = YES;
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim, opacityAnim, nil];
animGroup.duration = 0.5;
[anim.layer addAnimation:animGroup forKey:nil];
// self.tabBarItem.image=[UIImage imageNamed:#"Coffee_smoke.png"];
}

Resources