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...
Related
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];
}
}
I have a tableview and when i scroll to the bottom of it and release my finger, only half of the cell is visible. If i keep draggaing towards the bottom the whole cell show and then hides when i release my finger.
Any idea?
The frame of your tableView seems to be set to a wrong frame. Reset the frame like this:
CGRect tableViewFrame = [tableView frame];
tableViewFrame.size.height -= heightOfButtonBar;
tableViewFrame.origin.y += heightOfButtonBar;
[tableView setFrame: tableViewFrame];
I implemented a TableView to show up a PickerView when a particular row is selected. But that would sometime block the selected cell. So, I want my scrollView to scroll to the selected cell when the pickerView shows up.
Is there anyway to get the current location of the selected row? Such that I can implement something like
//---scroll to the current text field---
CGRect textFieldRect = [currentTextField frame];
[scrollView scrollRectToVisible:textFieldRect animated:YES];
Thanks.
If you want to solve the pickerView or the keyboard hiding the screen, you can try this approach. Implement the heightForFooterInSection and set a height value, and the tableView will scroll up to the same value as you specify there.
-(CGFloat)tableView:(UITableView*)tableView heightForFooterInSection:(NSInteger)section
{
return 70.0;
}
If you just want the tableView to scroll , just implement the scrollToRowAtIndexPath:atScrollPosition:animated:
Edit:
Since you are using a UIScrollView, you can programmatically scroll to a certain region using
[scrollView setContentOffset:CGPointMake(x, y) animated:YES];
Also read through this reference: http://www.aaron.griffith.name/weblog/2011/02/23/scrolling-a-uiscrollview-programmatically/
I have a problem and I hope one of you can suggest a better way of accomplishing what I am trying to do.
So I have a UITableView with a list of items. Each tableview row has an "Add" button. When the add button is pushed, the selected item is added to master list. (Think of a to-do app), That works fine, no problems there. What I want is some type of notification that the item was added to the list. Such as a checkmark that appears on the same row that was added for two seconds then disappears. Here is my code:
-(void)addToList:(id)sender event:(id)event{
//This is the add button method on each cell.
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self.theTableView];
NSIndexPath *indexPath = [self.theTableView indexPathForRowAtPoint: currentTouchPosition];
//Check image is the image of the checkmark that I want to display when the user adds an item.
checkImage.alpha = 0.0f;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.7f];
checkImage.alpha = 1.0f;
checkImage.frame = CGRectMake(currentTouchPosition.x + 100, currentTouchPosition.y, 25, 25);
[UIView commitAnimations];
....[only relevant code shown]...
}
When the user taps the add button, the checkImage fades in and positions it self where the user touched the row + 100 pixels to the right. That works fine, but it does not position the checkmark in the center of the row, it could be in the center, near the top or near the bottom, depending on where the user tapped the add button. I need it in the center, or a different way to go about this. Any suggestions? Thanks!
Once you get the indexPath, get the cell using,
UITableViewCell * cell = [self.theTableView cellForRowAtIndexPath:indexPath];
CGSize cellSize = cell.contentView.frame.size;
and later,
CGPoint touchPositionInCell = [self.theTableView convertPoint:currentTouchLocation toView:cell.contentView];
CGPoint checkmarkCenter = CGPointMake(touchPositionInCell.x + 100, cellSize.height/2);
CGRect checkmarkFrame = CGRectZero;
checkmarkFrame.origin = checkmarkCenter;
checkmarkFrame = CGRectInset(checkmarkFrame, -12, -12);
checkImage.frame = checkmarkFrame;
[cell.contentView addSubview:checkImage];
This should position the checkmark in the vertical center.
And I hope you are aware of concept of reusable cells. Try to remember the indexPath of the cell the checkImage is in. If you see a request for a cell at that indexPath then add checkImage as its subview.
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.