In my application I have a feed with posts. Each post is represented by a cell in UITableView. A post has two states: read or unread. Each cell has an UIImageView, which has to display different images based on this state. I set this imageView's image in my table controller's tableView:cellForRowAtIndexPath: method.
I also check visibility of cells while scrolling the table, and, when a cell becomes visible, it's state is changed, so cell's image is changed with animation. This animation, however, is not working properly when cell is reused. First cell to become visible is animated properly, but all cells after the first one already have resulting image when they appear on screen, animation is not fired for them. Here's how I'm doing it:
// This is called when a cell becomes visible
- (void)setIsVisible:(Boolean)isVisible
{
if (isVisible)
{
UIImage * toImage = [UIImage imageNamed:#"img-cell-read.png"];
[UIView transitionWithView:self.myImageView
duration:2.0f
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
self.myImageView.image = toImage;
} completion:NULL];
}
_isVisible = isVisible;
}
I realize that I should somehow reset this animation and start it again when I'm reusing a cell, but I don't understand how exactly should I do it. Also, if I try to animate cell's background color instead of image, it works fine with reusing.
You can override the prepareForReuse method in UITableViewCell to cancel any animations and also reset the image to the original state.
- (void)prepareForReuse{
[super prepareForReuse];
[self.myImageView.layer removeAllAnimations];
self.myImageView.image = YOUR_INITIAL_UIIMAGE;
}
also make sure to import quartz:
#import <QuartzCore/QuartzCore.h>
Related
In my tableview I need to animate the UI of every cell as the tableview reloads its data.
This needs to a custom animation, not just a fade or default transition.
For example, When tapping the 'Edit' button, in my view, the tableview reloads and each cell updates its UI for this new edit mode.
However, during this reload I have tried using a UIView animation block to update a constraint on a UILabel in my cells, but it will not animate.
Here is the code I run when I call reloadData on my tableview. This is called in cellForRowAtIndexPath method.
[UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^
{
//does not animate
_lblContainerLeftConstraint.constant = 18;
[self setNeedsLayout];
//does animate
_reorderIcon.alpha = 0.5f;
} completion:nil];
I think you need to do this in willDisplayCell rather than cellForRowAtIndexPath since that is called just before display which is as close as you can get to the point it actually appears on screen.
Then set the new constraint value before the animation block and also call [cell.contentView setNeedsLayout] to indicate that layout is needed on the cell content.
Finally only add [cell.contentView layoutIfNeeded] and your alpha change in the animation block which should animate the constraint change over your time period.
I'm using UICollectionView in one of my projects and when I try to call reloadData on the collection view, the cell order is always changed the same.
I create the cells something like that:
- (UICollectionViewCell *)collectionView:(UICollectionView *)aCollectionView
cellForItemAtIndexPath:(NSIndexPath *)aIndexPath {
PPhotoCVCell *cell =
[self._collectionView dequeueReusableCellWithReuseIdentifier:#"PPhotoCVCell"
forIndexPath:aIndexPath];
if (cell.photo == nil) {
PPhoto *photo = self._photos[aIndexPath.row];
cell.photo = photo;
}
cell.enableEditing = self._editing;
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
return cell;
}
The cell has two subviews of the class UIImageView, one for the image and another for an overlay if the cell is selected by the user.
When I'm in editing mode, the user can select cells. When some cells are selected and the user quits the editing mode, I set the alpha value of the overlay image view with an animation to 0.0 in the overwritten setSelected: method.
The problem is when I call the reloadData method on the collection view, the overlay image views in the selected cells hide without animation, but in other cells overlay image views appear without animation and disappear with the correct animation.
I detected that the image view for the photo is also changing when I call reloadData.
Is there any solution for this problem?
I ran into this same issue where each time I performed reloadData, the collection view would reverse it's order. Since I didn't see an answer here after 5 years, I'm posting my solution in case anyone else runs into this.
You actually have 2 options:
If you only need to reload a single cell, use the following instead of reloadData:
NSArray *thisItem = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:thisRow inSection:0]];
[self.collectionView reloadItemsAtIndexPaths:thisItem];
If you need to reload all data, use the following:
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
There's probably a reasonable explanation for this behavior somewhere, but I can't find one.
It sounds like your cells are being recycled. In your cell's prepareForReuse, make sure you're removing the overlay. Then add the overlay as appropriate in collectionView: cellForItemAtIndexPath:.
For reason of performance,I want to make a cached CGImageRef in static memory space.
So I have a static variable which cached an Image content like following,
UIImage *tempImage = [UIImage imageNamed:#"icon.jpg"];
static CGImageRef imageRef = nil;
if (!imageRef) {
imageRef = tempImage.CGImage;
}
Within UITableView Delegate method,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
UIImageView *tempImageView = [[UIImageView alloc] .....
tempImageView.layer.contents = (__bridge id)imageRef;
[cell.contentView addSubview:tempImageView]; // added this image into UITableViewCell as a SubView.
}
This will work at the initial display,
But if I selected a certain cell,the image on that cell will disappear after my selection.(Maybe there are some internal draw operations backing UITableViewCell),I guess that selection event making the cell to a Highlight state for displaying.
How to solve this problem without the following code?I do not want to assign a UIImage to a UIImageView directly.
tempImageView.image = tempImageView;
After writing some testing code,I had understood the problem happens when the selection event happens on a cell.CGImageRef assigned to a imageview's CALayer property added to the contentView will not disappear after selecting.
That is,when either selected or highlighted passed as a parameter is YES,UITableViewCell will make states of all its subviews to a selected state if they implemented their own behavior.Since UIImageView has several state for highlighting displaying,It will adjust presentation by itself internally.
So If your view simply has only one presentation state you could just set CALayer.contents on a simple UIView,instead of UIImageView.
If I subclass a UITableViewCell and override
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
//do nothing
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
//do nothing
}
I have a custom animation in:
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
of the UITableViewCell, something like:
[UIView animateWithDuration:0.15 animations:^{
self.center = CGPointMake(self.center.x + 110, self.center.y);
} completion:^(BOOL finished) {
isShifted = finished;
}];
The idea here is to shift the cell when the touch is down. Then if the cell is selected (no scrolling happened or cancel events) I proceed to another animation of the cell and transition to a new view.
The problem here is with an animation putting the cell back into its original position on setHighlighted:NO method call. Visually it looks like upon the selection the cell shifts (touch down), then starts going back (the system removes the highlight) and then starts the transition (the system calls setSelected:YES).
As far as I can see upon the selection of the cell in the table the following happens:
setHighlighted:YES
setHighlighted:NO
setSelected:YES
Is there any elegant solution to skip the call 2 and avoid the back-shifting-animation without introducing the timer that will check the selection?
You can think of using the delegate method tableView:shouldHighlightRowAtIndexPath:
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
// The cell has been touched, perform custom animation here
return NO;
}
setHighlight: will never be called on any cell, but you still have an entry point to perform the custom animation.
If you have a delegate for your UITableView (presumably your own view controller), you can catch the selection happening via something like "tableView: willSelectRowAtIndexPath:.
You can then do something "special" to your cell (such as set a property that forces the cell to do what you want in your animation) while the cell is selected.
And restore it back to what it was before when the cell is deselected via "tableView:willDeselectRowAtIndexPath:".
You also have the same type of delegate methods available for highlighting, e.g. "tableView:shouldHighlightRowAtIndexPath:"
I have a UIImageView that is supposed to cover the selected row from a UITableView. Both, the UIImageView, and UITableView are added to the main View inside Interface Builder. Instead of covering up the cell when it is selected, the UIImageView moves to the selected cell and disappears behind it. What I need it to do is move up to the selected row, and cover it up (i.e. the image should be in front of the cell, not behind it). Please also bear in mind that my image needs to only cover the selected row. Here is my relevant code:
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[UIView animateWithDuration:.3 animations:^{
//CGRect rect = [tableView rectForRowAtIndexPath:indexPath];
CGRect rect = [self.view convertRect:[tableView rectForRowAtIndexPath:indexPath] fromView:tableView];
_imageView.frame = rect;
}];
}
Can anyone see what I am doing wrong, and know how to fix this?
Thanks in advance to all who reply.
It is simply because the imageView is added before the table view in the subviews, I guess. Hence you can use bringSubviewToFront method and apply it to the imageView after you add your tableView to the subviews, for example.