Is there any way to animate the removal of a UITableView cell accessory?
I currently am showing a UITableViewCellAccessoryDisclosureIndicator, but I would like to animate swapping the disclosure indicator with a UISwitch on all visible table cells.
I've tried something like this:
[UIView animateWithDuration:0.3
animations:^{
for (SwitchTableViewCell *cell in self.tableView.visibleCells)
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
}];
... but unfortunately that has no affect. The disclosure indicator abruptly disappears and the contentView width jumps in one step, rather than a smooth transition.
accessoryType is not an animatable property. There are two ways you can do this, depending on your situation. The easiest only applies if you are changing the accessory to a UISwitch because of entering the editing state. In this case, just usecell.editingAccessoryType = theSwitch; in your tableView:cellForRowAtIndexPath: method. The table view will then do a fade in/out automatically when entering editing mode.
If you are doing this outside of editing mode, then the following code will do what you want:
[UIView animateWithDuration:0.3 animations:^{
for(SwitchTableViewCell *cell in self.tableView.visibleCells) {
[[cell valueForKey:#"_accessoryView"] setAlpha:0.0];
}
} completion:^(BOOL done) {
for(SwitchTableViewCell *cell in self.tableView.visibleCells) {
cell.accessoryView = theSwitch;
}
}];
However, I do not know if this code will make it into the app store since it uses the hidden property _accessoryView.
Related
I've got a UICollection view that has a bunch of cells, when the user presses down on one id like to have it animate a bit. The problem i'm running into is the animation competes with no duration. Its like it just ignores the animate all together.
Heres the code from the collection view. Cells is just the collection of cells that i'm using, I tried doing cellForItemAtIndexPath: but I could'nt get the method inside the cell to call. So i created an array and put each cell inside there when they are created.
-(void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"Highlighted");
[[cells objectAtIndex:indexPath.row] shrinkCell:YES];
}
And here is the code from inside the cell.m
-(void)shrinkCell:(BOOL)shrink{
NSLog(#"Shrink");
if (shrink) {
[UIView animateWithDuration:1.0
delay:0.5
options: UIViewAnimationOptionCurveEaseIn
animations:^{
self.bg.frame = CGRectOffset(self.bg.frame, 100, 100);
}
completion:nil];
}
}
So just to reiterate. The cell does move, it just doesn't animate. Any thoughts?
Thanks!
I have a table view with a fairly complex cell. The cell includes a button that when pressed, should expand the cell and present some extra controls. I thought this would be pretty simple, so I wrote the following code (in my class derived from UITableViewCell):
self.extraView = [[MyView alloc] initWithFrame:CGRectMake(0,100,300,200)];
self.extraView.clipsToBounds = YES;
[self addSubview:self.extraView];
self.extraView.transform = CGAffineTransformMakeScale(1, 0.1f);
[UIView animationWithDuration:0.5 animations:^{
[tableView beginUpdates];
[tableView endUpdates];
self.extraView.transform = CGAffineTransformIdentity;
}];
Surprisingly, this makes extraView flash briefly on screen and then disappear. If I remove the calls to beginUpdates and endUpdates then extraView animates exactly as I expected. However, the table cell is not large enough to display it. I tried setting alpha to 0 and then fading it in during the table update, and that seems to work fine. Unfortunately, I am supposed to make the extraView grow in place and not fade.
I have played with various ways of modifying extraView such as changing the frame, but the table updates always produce some side effect. I also tried chaining the scale change in the completion handler, which of course didn't work either. I think that is because the table view is not done animating when the completion block is executed.
Is there any way to animate the frame of a view in a cell during a table update?
It appears that the answer is no. At least, I have not found anything that will look visually correct when modifying the view frame during the table view update. What I do instead now is wait a bit and then grow the view's frame. This is how the code looks:
extraView.alpha = 0; // hide the view immediately after adding it
[tableView beginUpdates];
[tableView endUpdates];
[UIView animateWithDuration:0.5 animations:^{
button.alpha = 0; // fade out old button
} completion:^(BOOL finished) {
extraView.alpha = 1; // reappear, but not fading in
extraView.transform = CGAffineTransformMakeScale(1, 0.01f);
[UIView animateWithDuration:0.5 animations:^{
extraView.transform = CGAffineTransformIdentity; // animate scale back to original size
}];
}];
This not exactly the effect I was trying to create, but it is the closest I have been able to get. I'm posting it as the answer in case anyone else encounters a similar issue.
I had a similar issue, and wanted to open / close and change specific elements in a cell by animation based on selection.
I did this by first of all creating a subclass for the uitableviewcell.
Each tableviewcell has an open and close method which does an animation of the backgroundcolor and the size (among other things).
The open and close methods of the tableviewcell look like this:
- (void)open {
[UIView animateWithDuration:0.3
animations:^{
[self.customBackgroundView setHeight:76];
self.deleteButton.alpha = 1;
self.selectedIndicator.transform = CGAffineTransformMakeRotation(M_PI);
[self.selectedIndicator setY:60];
}
];
}
- (void)close {
[UIView animateWithDuration:0.3
animations:^{
[self.customBackgroundView setHeight:40];
self.deleteButton.alpha = 0;
self.selectedIndicator.transform = CGAffineTransformMakeRotation(0);
[self.selectedIndicator setY:24];
}
];
}
It also has a setSelected method, so when the cell is selected it opens:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if (selected) {
[self open];
} else {
[self close];
}
}
Now, only thing left, is to make sure to close other cells, when this cell is selected.
And call the tableview beginUpdates and tableview endUpdates:
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (_currentSelection == indexPath.row) {
_currentSelection = -1;
SOColumnTableViewCell *cell = (SOColumnTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
[cell close];
} else {
_currentSelection = (int)indexPath.row;
}
[tableView beginUpdates];
[tableView endUpdates];
}
}
just as a background, this question is related to this one I posted earlier: Trying to expand/collapse UITableViewCell from a UIButton on the custom cell
To summarize, I have a UITableView with several custom table cells. Each custom UITableViewCell has a text area, followed by a "View More" button. Essentially, each cell will initially display 3 lines of text, but when the user taps the "View More" button, the cell should expand to the entire text area height. Tapping the button again will collapse the cell.
I think this is difficult to visualize, so I have taken a short video here: http://dl.dropbox.com/u/762437/cell-animation.mov
(it's about a 3.5mb file, so shouldnt take that long to load). In the video, I show a cell doing the expand/collapse animation. When doing the expanding, the "View More" button animates downward. When tapping it again, it just jumps back up to its original position with no animation. Edit: Sorry, I should be more clear. The UITableView is animating the cells correctly, what I am asking is how to make the "View More" button animate correctly.
I've got the expand/collapse working (whether I did it right or wrong is another matter!), but the animations arent working as I expect. The expanding animation works, but not the collapsing.
In each custom UITableViewCell class, I have a IBAction that is called when the user taps the "View More" button. I also keep track of the cells that are currently expanded in a NSMutableArray. When the user taps the "Close" button, that cell is removed from the array.
In my tableView's cellForRowAtIndexPath, I check the array to see which cells should be expanded and which should show at their default size.
I am doing so with this code:
// Check if the array contains the particular cell, if so, then it needs to be expanded
if([expandedRows containsObject:indexPath])
{
// Expand the cell's text area to fit the entire contents
[cell.textLabel sizeToFitFixedWidth:cell.textLabel.frame.size.width];
// Find the new Y-position of where the "Close" button.
// The new Y position should be at the bottom of the newly expanded text label
CGFloat bottomYPos = cell.textLabel.frame.origin.y + cell.textLabel.frame.size.height;
// Get a rect with the "Close" button's frame and new Y position
CGRect buttonRect = cell.showAllButton.frame;
buttonRect.origin.y = bottomYPos;
// Animation block to shift the "Close" button down to its new rect
[UIView animateWithDuration:0.3
delay:0.0
options: UIViewAnimationCurveLinear
animations:^{
cell.showAllButton.frame = buttonRect;
[cell.showAllButton setTitle:#"Close" forState:UIControlStateNormal];
}
completion:^(BOOL finished){
NSLog(#"Done 1!");
}];
}
else
{
// This cell is currently collapsed
// Get the button rect and subtract the height delta to put it back
// OriginalCellSizes is where I keep track of the original Y positions
CGRect buttonRect = cell.showAllButton.frame;
buttonRect.origin.y -= cell.frame.size.height - [[originalCellSizes objectAtIndex:indexPath.row] floatValue];
// Animation block for the Button
[UIView animateWithDuration:0.3
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{
cell.showAllButton.frame = buttonRect;
[cell.showAllButton setTitle:#"View More" forState:UIControlStateNormal];
}
completion:^(BOOL finished){
NSLog(#"Done! 2");
}];
}
Upon investigating, I found that in the "else" branch of that if-else, the buttonRect is already at the same Y position as the original position. I suspect that this is why there is no animation happening, bu I'm not sure.
Any help would be really great!
simple way to do this......
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:insertIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView deleteRowsAtIndexPaths:deleteIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
insertIndexPaths is an array of NSIndexPaths to be inserted to your table.
deleteIndexPaths is a array of NSIndexPaths to be deleted from your table.
Example array format for index paths :
NSArray *insertIndexPaths = [[NSArray alloc] initWithObjects:
[NSIndexPath indexPathForRow:0 inSection:0],
[NSIndexPath indexPathForRow:1 inSection:0],
[NSIndexPath indexPathForRow:2 inSection:0],
nil];
got it from this question...this question...
I've implemented Cocoa with Love's example for Multi-row selection which involves creating a custom UITableViewCell that initiates an animation in layoutSubviews to display checkboxes to the left of each row, like so:
- (void)layoutSubviews
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[super layoutSubviews];
if (((UITableView *)self.superview).isEditing)
{
CGRect contentFrame = self.contentView.frame;
contentFrame.origin.x = EDITING_HORIZONTAL_OFFSET;
self.contentView.frame = contentFrame;
}
else
{
CGRect contentFrame = self.contentView.frame;
contentFrame.origin.x = 0;
self.contentView.frame = contentFrame;
}
[UIView commitAnimations];
}
This works fine and for all intents and purposes my UITableView acts as it should. However I'm running into a small aesthetic issue: when scrolling my UITableView rows which have not previously been displayed will initiate their sliding animation, meaning the animation is staggered for certain rows as they come into view.
This is understandable, given that setAnimationBeginsFromCurrentState has been set to YES and rows further down in the UITableView have yet to have their frame position updated. To solve the issue, I attempted to use willDisplayCell to override the animation for cells which become visible while the UITableView is in edit mode. Essentially bypassing the animation and updating the rows frame immediately, so as to make it appear as if the cell has already animated into place, like so:
/*
Since we animate the editing transitions, we need to ensure that all animations are cancelled
when a cell is scheduled to appear, so that things happen instantly.
*/
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
[cell.contentView.layer removeAllAnimations];
if(tableView.isEditing) {
CGRect contentFrame = cell.contentView.frame;
contentFrame.origin.x = EDITING_HORIZONTAL_OFFSET;
cell.contentView.frame = contentFrame;
} else {
CGRect contentFrame = cell.contentView.frame;
contentFrame.origin.x = 0;
cell.contentView.frame = contentFrame;
}
}
Unfortunately this doesn't seem to have any effect. Does anyone have any idea as to how I can solve this issue?
Not sure if you still need an answer to this question but I just ran into the exact same issue so I thought that I would share my solution. I implemented Multi-Selection the same way its described in the Cocoa with Love blog post that you mentioned.
In the cellAtIndexPath DataSource method when I create a new cell (not if the cell is already in the Queue of reusable cells) I check if the tableView is in editing mode and if it is I set a property on the cell (I created my own custom cell with an EnableAnimation property) to false so when it gets the SetEditing callback it will not animate the cell, instead it will just set the frame. In the constructor of the Cell class I set EnableAnimation to true, when the SetEditing callback is called I set EnableAnimation to the animate argument that is passed in. I hope this helps.
In -tableView:heightForRowAtIndexPath: I'm changing the height for a cell dynamically during usage of the table. But I don't want this to happen like BAAANNGGG..., I want it nicely animated. Where would I put the animation block? Would that go just around the -reloadData call?
Well, at least this does nothing:
[UIView beginAnimations:#"foo" context:nil];
[UIView setAnimationDuration:3];
[self.table reloadData];
[UIView commitAnimations];
don't get any animation here...
Potentially the following is a better option if you're on iOS 3.0 and up:
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
Change the animation to match your preference of course...