I'm trying to animate two table cells background colors from red back to the original color, white.
The following code is what I'm using. The problem is that it never shows the red color -- it simply animates from (original) white to (animated-to) white. I.e., if I change the color in the animation block, it will animate to that color.
[table cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]].backgroundColor = [UIColor redColor];
[table cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]].backgroundColor = [UIColor redColor];
[UIView animateWithDuration:0.8 delay:0.2 options:UIViewAnimationOptionCurveEaseInOut animations:^{
[table cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]].backgroundColor = [UIColor whiteColor];
[table cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]].backgroundColor = [UIColor whiteColor];
}
completion:^(BOOL finished) {
}];
The following code works as expected for the table's background, but that's not what I want:
table.backgroundColor = [UIColor redColor];
[UIView animateWithDuration:0.8 delay:0.2 options:UIViewAnimationOptionCurveEaseInOut animations:^{
table.backgroundColor = [UIColor clearColor];
}
completion:^(BOOL finished) {
}];
So, why doesn't my cell background animate when my table background does?
For what it's worth, I have a bunch of other chained animations I perform on the table view right after this, but having commented those animations out, I still have this issue.
Because a cell contains other views. You have to check the color of cell.contentView, cell.accessoryView and cell.backgroundView. If all of these are [UIColor clearColor], you can just animate cell.backgroundColor.
It looks like #Jason Coco is right. I can't do it (cleanly and quickly):
From Apple:
Note: If you want to change the background color of a cell (by setting
the background color of a cell via the backgroundColor property
declared by UIView) you must do it in the
tableView:willDisplayCell:forRowAtIndexPath: method of the delegate
and not in tableView:cellForRowAtIndexPath: of the data source.
Changes to the background colors of cells in a group-style table view
has an effect in iOS 3.0 that is different than previous versions of
the operating system. It now affects the area inside the rounded
rectangle instead of the area outside of it.
Reference
So if I really wanted to I'd have to set some variable, then call reloadData on the table or something similar.
Related
In table view, I am adding new cells (to the top of table view) with animation (background color of cells changing from gray to default white). All works fine, but it animates only cells, that is visible on the screen. So, if count of rows more that display can show, it doesn't animate the rest of cells.
This is my code for animating cells (I use it in my custom update table view method, after insertion of new rows. InsertingArray consists of IndexPath's of rows that should be animated):
for (int i=0; i<[insertingArray count]; i++)
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[insertingArray objectAtIndex:i]];
cell.backgroundColor = [UIColor grayColor];
[UIView animateWithDuration:1.0
delay: 1.0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
cell.backgroundColor = [UIColor whiteColor];
}
completion:nil];
}
So, how fix it and animate all rows, that was added in table view?
I am working with UICollectionView where I am displaying all the images from photo library of the phone.
When I click on any of the image, the image get flipped and and some information regarding the image is displayed.
When the user again clicks on the same image the image flips again and the original image is shown.
The problem is that whenever I scroll down through the UICollectionView the last selected image flips automatically and the information about the image gets displayed.
How to stop this problem.
Here is some code:
- (void) collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell1 = [collectionView cellForItemAtIndexPath:indexPath];
if(old_path!=NULL){
UICollectionViewCell *cell2 = [collectionView cellForItemAtIndexPath:old_path];
[UIView transitionFromView:cell2.selectedBackgroundView toView:cell2.contentView duration:0.5 options:UIViewAnimationOptionCurveLinear |UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
}
if(old_path==indexPath&&flag)
{
[cell1 setSelected:NO];
[UIView transitionFromView:cell1.selectedBackgroundView toView:cell1.contentView duration:0.5 options:UIViewAnimationOptionCurveLinear |UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
flag=FALSE;
}
else{
[UIView transitionFromView:cell1.contentView toView:cell1.selectedBackgroundView duration:0.5 options:UIViewAnimationOptionCurveLinear |UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
flag=TRUE;
}
old_path=indexPath;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
ALAsset *asset = assets[indexPath.row];
NSLog(#"Description : %#",[asset description]);
UIImage *img=[self imageWithImage:[UIImage imageWithCGImage:[asset thumbnail]] convertToSize:CGSizeMake(150, 150)];
UIView *contents = [[UIView alloc]initWithFrame:cell.bounds];
contents.backgroundColor = [UIColor colorWithPatternImage:img];
[cell.contentView addSubview:contents];
UIView *backgroundView = [[UIView alloc]initWithFrame:cell.bounds];
backgroundView.backgroundColor = [UIColor yellowColor];
UIButton *del=[UIButton buttonWithType:UIButtonTypeRoundedRect];
del.frame= CGRectMake(backgroundView.frame.origin.x+20, backgroundView.frame.origin.y+20, 100, 40);
[del setTitle:#"Delete" forState:UIControlStateNormal];
[del addTarget:self action:#selector(delete) forControlEvents:UIControlEventTouchUpInside];
[backgroundView addSubview:del];
UIButton *cancel=[UIButton buttonWithType:UIButtonTypeRoundedRect];
cancel.frame= CGRectMake(backgroundView.frame.origin.x+20, backgroundView.frame.origin.y+80, 100, 45);
[cancel setTitle:#"Cancel" forState:UIControlStateNormal];
[cancel addTarget:self action:#selector(cancel) forControlEvents:UIControlEventTouchUpInside];
[backgroundView addSubview:cancel];
cell.selectedBackgroundView = backgroundView;
[cell bringSubviewToFront:cell.selectedBackgroundView];
return cell;
}
Here, old_path contains the index of the last selected image.
The main problem is probably with the UIView transition methods:
[UIView transitionFromView:cell1.selectedBackgroundView toView:cell1.contentView duration:0.5 options:UIViewAnimationOptionCurveLinear |UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
UICollectionViewCell's contentView and selectedBackgroundView shouldn't be messed with like this, because the cell manages their layout. This code will remove the content view entirely and replace it with the background view, which is expected to be behind the content view when selected, and removed from the view hierarchy when not selected.
The proper way to accomplish what you're doing (showing / hiding the image in response to a tap) would be perform the transition between the image view itself and another subview on the content view.
There could also be something in your resizing code that's flipping the image, but it's hard to say without the code for imageWithImage:convertToSize:. It would probably be more efficient to get rid of that method, and do something like this:
UIImageView *imageView = [[UIImageView alloc] initWithFrame:cell.bounds];
imageView.contentsMode = UIViewContentModeScaleAspectFill;
imageView.clipsToBounds = YES;
imageView.image = [UIImage imageWithCGImage:[asset thumbnail]];
[cell.contentsView addSubview:imageView];
A couple of other observations:
Collection view cells are reused, which means that your implementation collectionView:cellForItemAtIndexPath: could end up adding a whole pile of image views to a cell that has been dequeued several times. A better solution would be to subclass UICollectionViewCell and add the custom views in its init method.
The code old_path==indexPath does not actually test for equality between the two index paths, just if the two variables have the same address in memory. Use [oldPath isEqual:indexPath] instead.
You cannot do custom animations on background views without subclassing and handling explicitly. Because collection view automatically brings the selectedBackgroundView to top if selectedBackgroundView is not same as backgroundView. Adding apple documentation for reference.
// The background view is a subview behind all other views.
// If selectedBackgroundView is different than backgroundView, it will be placed above the background view and animated in on selection.
https://developer.apple.com/library/iOs/documentation/UIKit/Reference/UICollectionViewCell_class/Reference/Reference.html
Theoretically the following code should animate the table view cell of the screen to the right and bring in a dark "view" in it's place.
CGPoint location = [gesture locationInView:tableView];
NSIndexPath *swipedIndexPath = [tableView indexPathForRowAtPoint:location];
UITableViewCell *swipedCell = [tableView cellForRowAtIndexPath:swipedIndexPath];
//code to create view
UIView *sideView;
sideView.backgroundColor = [UIColor lightGrayColor];
//set the side view frame to the same as the cell
sideView.frame = swipedCell.frame;
//add it to the tableview
[tableView addSubview:sideView];
[UIView animateWithDuration:1
animations:^{
sideView.frame = CGRectMake(0, swipedCell.frame.origin.y, swipedCell.frame.size.width, swipedCell.frame.size.height);
// While simultaneously moving the cell's frame offscreen
// The net effect is that the side swipe view is pushing the cell offscreen
swipedCell.frame = CGRectMake(swipedCell.frame.size.width, swipedCell.frame.origin.y, swipedCell.frame.size.width, swipedCell.frame.size.height); //move cell off
}];
However, only the cell moves off the screen. No gray view comes in it's place.
Is there a step I am missing? What is wrong with this code?
Video of example here
The big error is that you're not initializing sideView to anything.
Try UIView* sideview = [[UIView alloc] initWithFrame:swipedCell.frame];
It doesn't sound like a good idea to add a view in place of a cell just like that. You'd have to deal with scrolling, table view editing, and other stuff that the UITableView takes care of for you. So instead, try adding the sideView as a subview of swipedCell.contentView and then doing this animation instead:
[UIView animateWithDuration:1 animations:^{
sideView.frame = CGRectMake(0, swipedCell.frame.origin.y, swipedCell.frame.size.width, swipedCell.frame.size.height);
//This moves all the subviews except for the sideView off the screen
for (UIView *subview in swipedCell.contentView.subviews)
if (![subview isEqual:sideView])
subview.frame = CGRectOffset(subview.frame, swipedCell.frame.size.width, 0.0);
}];
Hope this helps!
I wrote a custom UITableViewCell - image view, button and three labels now I am trying to add some animations to it. So once I tap the button, it fades away and the spinner replaces the button. After two seconds the cell is overlaid with a red color, as a subview of cell fades in, then the indicator is removed and and red overlay starts fading back out. As well the button I previously removed fades back in.
(I could not phrase it a better way :P )
The method is :
-(void)rentButtonPressed:(id)sender
{
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
[indicator startAnimating];
indicator.center = self.rentButton.center;
[UIView animateWithDuration:0.2
animations:^{self.rentButton.alpha = 0.0;}
completion:^(BOOL finished){
[self.rentButton removeFromSuperview];
[self addSubview:indicator];
}
];
UIView *overlay = [[UIView alloc] initWithFrame:self.backgroundImage.frame];
overlay.alpha = 0.0;
overlay.backgroundColor = [UIColor redColor];
[self.contentView addSubview:overlay];
[UIView animateWithDuration:0.4
delay:2.0
options:UIViewAnimationCurveEaseInOut
animations:^{
[indicator removeFromSuperview];
overlay.alpha = 0.4;
}
completion:^(BOOL finished){
[UIView animateWithDuration:0.4
animations:^{ overlay.alpha = 0.0; }
completion:^(BOOL finished)
{
[overlay removeFromSuperview];
}
];
[self.contentView addSubview:self.rentButton];
[UIView animateWithDuration:0.4 animations:^{ self.rentButton.alpha = 1.0;}];
[self.delegate didTryToRentMovieAtCell:self];
}
];
}
So the code does fade out the button, replace it with spinner and fades in the red overlay. The problem is, the red overlay does not fade away, but disappears same with the button, instead of fading in, it just appears.
During your animation, you are changing the view hierarchy by adding and removing subviews. The UIView class method animateWithDuration:animations:completion is intended only animating property changes in a view, and not for changing the view hierarchy.
Try using the UIView class method transitionWithView:duration:options:animations:completion: instead, and use the cell's content view as the "container."
This documentation is helpful in distinguishing between animating view property changes and animating view transitions, specifically the section "Changing the Subviews of a View":
http://developer.apple.com/library/ios/#documentation/windowsviews/conceptual/viewpg_iphoneos/animatingviews/animatingviews.html
I have a label that gets updated with values calculated from information received by the server. Depending on how much traffic there is the updates can come in rapidly or at random intervals. What I would like to do is compare the new calculated value with the old value and if it has increased then change the text to be green and the background to be a darker green and if the value has decreased then change the text to a shade of red and dark red.
That part is easy to do, what I am having some trouble is after a half a second or so I would like to change the background and text to be their default colors. I'm doing this as part of user feedback for when the values change.
Any information would be greatly appreciated.
A label is a subclass of UIView. Assuming you have some method that notifies you of when the change occurs...
- (void)someMethodThatNotifiesOfChange {
// Calculate new values and assume the result means color button green
[UIView animateWithDuration:0.5 animations:^{
label.backgroundColor = [UIColor greenColor];
} completion:^(BOOL finished) {
if (finished) {
[UIView animateWithDuration:0.5 delay:5.0 options:nil animations:^{
label.backgroundColor = [UIColor clearColor];
} completion:nil];
}];
}
This just changes the background to be green, but illustrates the point none-the-less. This take 0.5 seconds to animate to green. Then, when it is complete, it waits 5 seconds and then takes 0.5 to animate back to clear.
You can use CoreAnimation library if you want advanced animation but if you just animate UILabel properties, you can use UIView static method to do this. You can do it like this:
[UIView animateWithDuration:0.3f animations:^{
label.textColor = [UIColor greenColor];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3f animations:^{
label.textColor = [UIColor blackColor];
} completion:nil];
}];
In first animation block, you set label's text color (and background color if you want). In its completion block you set the label's color back using another animation.
Hope it helps.
Take a look at NSTimer. Timers are useful to lots of situations!
Good luck!
Alternatively by IOS 4 the followwing block would do the same effect
[UIView transitionWithView: infoLabelInternet duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
infoLabelPremium.textColor = kPopoverActiveTextColor;
} completion:nil];
You can also do it this way:
label.backgroundColor = [UIColor greenColor];
[label performSelector:#selector(setBackgroundColor:) withObject:[UIColor clearColor] afterDelay:0.5];