I'm actually going to DELETE a cell on aTableView when a cell on bTableView is deselected. And also, I'm using UIViewController instead of UITableViewController just because there'r lots of things on my app, and for sure, I've adopted UITableViewDelegate, UITableViewDataSource.
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
if (tableView == bTableView) { ......
MyCustomCell *cell = (MyCustomCell *)[aTableView cellForRowAtIndexPath:indexPath];
[convertResultTableView beginUpdates];
// some data updates stuff..
[UIView beginAnimations:#"quitRotation" context:NULL];
CATransform3D rotation;
rotation = CATransform3DMakeRotation((70.0 * M_PI) / 180, 0.0, 0.0, 1.0);
cell.layer.transform = rotation;
cell.layer.anchorPoint = CGPointMake(-1, 0.5);
cell.alpha = 0;
cell.layer.shadowOffset = CGSizeMake(0, 0);
[UIView commitAnimations];
[convertResultTableView endUpdates];
The problem is the cells animate proper only when it animate at the second, third, forth.. time but NOT the first time. (which means it appear, disappear, appear and disappear <- this time)
It still do some animation but it is just not as what I expected, and there'r a lot different between that WRONG animation and the proper animation.
Any ideas?
You should try to do this animation in method cellForRowAtIndexPath
Something like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.selectedIndexPath = indexPath;
[tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCustomCell *cell = (MyCustomCell *)[tableView cellForRowAtIndexPath:indexPath];
if([self.selectedIndexPath isEqual: indexPath]) {
// do the animation
}
}
Really a silly mistake!
Here's my mistake:
My aTableView will do some animation when first show, but my code have some silly mistake so that only my first row of cell of the aTableView do the animate, therefore, cell.layer.position will be different. However, after it disappear and appear again, the cell.layer.position all go to the right place because the code is right, so it works when it's 2th, 3rd... time.
How I found the mistake: I just do NSLog(...); to log basically all the property of cell.layer because I'm really new to CoreAnimation, fortunately, my first try is cell.layer.position, and I found the mistake immediately.
Related
I am trying to perform an animation on a cell when the accessory view is tapped. The tapped delegate method is firing and I can get the row to do something--change label, but it is ignoring the animation (or in another case--not even making the change.) How can I get the animation to work properly?
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath{
MyCustomCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
[UIView animateWithDuration:5.0
delay: 5.0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
//NEXT TWO LINES HAVE NO EFFECT ON CELL SO COMMENTED OUT
//cell.nameLabel.text = #"Thank you. FIRST ANIMATE TRY";
// [self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
//NEXT THREE LINES CHANGE TEXT BUT WITHOUT ANIMATION
[self.tableView beginUpdates];
cell.nameLabel.text = #"Thank you. Second try!";
[self.tableView endUpdates];
}
completion:^(BOOL finished){
NSLog(#"animation finished");
}];
}
BTW I also tried explicitly dispatching this in the main queue but it had no effect. It should already be in main queue.
First of all, you don't need to call beginUpdates or endUpdates. Second, you can't animate the change of a label's text value.
What you need to is have a label on the cell and initialize the alpha property to 0.0. When accessoryButtonTappedForRowWithIndexPath is called set the alpha property to 1.0 inside of your animation block.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
AwesomeCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.thankYouLabel.text = #"Thank you";
cell.thankYouLabel.alpha = 0.0;
return cell;
}
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
AwesomeCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[UIView animateWithDuration:1.0 animations:^{
cell.thankYouLabel.alpha = 1.0;
}];
}
I have a table view with a prototype cell designed in ViewController scene of storyboard.
Now my objective is when the app will start and the view will be loaded then the data in tableview should not come at once rather it will come one by one with animation.
Here is the code how I am doing my animation:-
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (![shownIndexes containsObject:indexPath]) {
[shownIndexes addObject:indexPath];
[UIView animateWithDuration:2.0f delay:0.0f usingSpringWithDamping:0.5f initialSpringVelocity:1.0f options:UIViewAnimationOptionAllowAnimatedContent animations:^{
vw.frame = frame;
} completion:^(BOOL finished) {
}];
}
Other methods:-
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
return 1; //count of section
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 20; //count number of row from counting array hear cataGorry is An Array
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 120;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"CellA";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
The following code is animating the first 5 cells which are displaying in tableview initially at once , but after that when I am scrolling it is happening one by one.
So major issue is
How to display the rows or cells in UITableview one by one with animation not at once?
Any info or help will be appreciated.
Thanks.
Tell the table first that in only has one row, in numberOfRows…. After a delay, return 2 instead of 1. And so on.
I have UITableViewCell with detailed arrow that points to top or to down.
Than I tap on this cell I want to expand it. So I will reload UITableView and I want to animate rotation ofUIImageView`.
Question - is it possible to animate rotation of UIImageView simultaneously with reloading data in UITableView?
Yes, you can create a table with expandable/contactable cell's, also while expanding you can animate the containers of the cell's.
But, one thing until and unless it is a requirement to reload table, I'll suggest you to reload row with the animations performed simultaneously.
Also as you asked in comment what's the best way to track whether the cell needs to be shown simple or detailed, I think it depends on your requirements, like if you need to show only one cell expanded at a time, then you just keep the index of current expanded cell in the class else if you need to show more than one cell expanded at a time than you can use the data source to keep the information whether the cell needs to be show as simple or detailed. If you do not have the data source then you can do the same by creating a list of cell's index path keeping track of detailed cells.
Programmatically you need to do following things to show simple/ detailed cells-
Note: I have assumed to show only one cell expanded at a time.
1) Create a member variable to keep track of expanded cell.
NSIndexPath *expandedCellIndexPath;
2) Return height for expanded cell(if expanded) otherwise for simple cell
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if([expandedCellIndexPath compare:indexPath] == NSOrderedSame)
return 100.0f;
return 50.0f;
}
3) Create cell as-
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"cellId";
CustomCell *customCell = nil;
customCell = (CustomCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(customCell == nil)
{
NSString *nibName = #"CustomCell_iPhone";
customCell = (CustomCell *)[[[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil] lastObject];
customCell.selectionStyle = UITableViewCellSelectionStyleNone;
}
if([expandedCellIndexPath compare:indexPath] == NSOrderedSame)
{
[customCell.m_arrowImageView setImage:[UIImage imageNamed:DOWN_ARROW_KEY]];
[self showSelectedCellAnimations:customCell];
}
else
{
[customCell.m_arrowImageView setImage:[UIImage imageNamed:UP_ARROW_KEY]];
[self showUnSelectedCellAnimations:customCell];
}
// you can also set default up arrow image and rotate it with down arrow image and vice versa
return customCell;
}
4) Rotate Up/Down arrow image using-
- (void)rotateImage:(UIImageView *)image duration:(NSTimeInterval)duration
curve:(int)curve degrees:(CGFloat)degrees
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve:curve];
[UIView setAnimationBeginsFromCurrentState:YES];
CGAffineTransform transform =
CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(degrees));
image.transform = transform;
[UIView commitAnimations];
}
5) Call above method as-
[self rotateImage:customCell.m_helpImage duration:0.25
curve:UIViewAnimationCurveEaseIn degrees:0.0];
6) Inside didSelectRowAtIndexPath: set the expandedCellIndexPath to the current index path which needs to be expanded and reload previous expanded row to contract and reload current expandedCellIndexPath row to expand, like-
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
CustomCell *customCell = nil;
customCell = (CustomCell *)[tableView cellForRowAtIndexPath:indexPath];
NSIndexPath *previousIndexPath = expandedCellIndexPath;
if([expandedCellIndexPath compare:indexPath] != NSOrderedSame)
expandedCellIndexPath = indexPath;
[self.m_tableView reloadRowsAtIndexPaths:#[previousIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.m_tableView reloadRowsAtIndexPaths:#[expandedCellIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.m_tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
I would like to make something like expandable cells. Result I'm trying to achieve is:
Actually I have:
Here is my code:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
DoctorCell *cell = self.prototypeCell;
DoctorModel *doctor = [self.fetchController objectAtIndexPath:frcIndexPath];
// depending on isActive property I add (or do not add) some content.
[cell cellForDoctor:doctor isActiveCell:[self.activeCellIndexPath isEqual:indexPath]];
[cell setNeedsUpdateConstraints];
[cell setNeedsLayout];
[cell layoutIfNeeded];
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height + 1;
}
Here is my cell selection method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
NSIndexPath *oldIndexPath = self.activeCellIndexPath;
if ([indexPath isEqual:self.activeCellIndexPath]) {
self.activeCellIndexPath = nil;
oldIndexPath = nil;
} else {
self.activeCellIndexPath = indexPath;
}
[self.doctorTableView beginUpdates];
[self.doctorTableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, oldIndexPath, nil] withRowAnimation:UITableViewRowAnimationNone];
[self.doctorTableView endUpdates];
[self.doctorTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
As you can see, my animation looks weird (kind of moving to wrong position, jumps, etc). What I'm doing wrong? Or do I need to pick another approach for expanding cells?
You can use UITableViews animation automation like this:
Call [tableView beginUpdates];
Change the data source.
Insert or delete rows and sections, or reload them
This should cause new heights to be calculated, etc.
Make sure you use something appropriate for the withRowAnimation parameter of these methods
Call [tableView endUpdates];
Your animation should now perform as expected.
Maybe your problem is that you selected UITableViewRowAnimationNone for the row animation.
I have a problem with the default separator on UITableView in iOS 7.
When used as default the first and last separators have no insets, others are a bit inset. The original situation can be seen below:
Everything is ok. The first and last separators spread through the entire width of the table while the others are a bit smaller. Now I have the table view set to editing and I allow the user to reorder cells. And when the user does so the separators get messed up and do not appear correctly. The situation can be seen on the images below:
Do I really need to reload the data in order to fix this issue or is it an iOS 7 bug or am I doing something wrong?
How to fix this?
EDIT
Added some info about my implementation. I return NO on - (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath and UITableViewCellEditingStyleNone on - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath. My - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath is:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
cell.shouldIndentWhileEditing = NO;
cell.textLabel.font = [UIFont someFont];
UIColor *color = [UIColor randomColor];
cell.textLabel.text = #"Some text";
CGRect rect = CGRectMake(0, 0, 15, 15);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[color set];
CGContextFillEllipseInRect(ctx, rect);
cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return cell;
}
and
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
if (sourceIndexPath.row == destinationIndexPath.row) return;
NSString *tmp = [itemOrder objectAtIndex:sourceIndexPath.row];
[itemOrder removeObjectAtIndex:sourceIndexPath.row];
[itemOrder insertObject:tmp atIndex:destinationIndexPath.row];
didReorder = YES;
}
This seems to be a problem in the iOS SDK. One way to solve it would be to manually apply the correct separator insets to all cells. However, I personally prefer to let the system do that.
If you reload a cell after reordering, the correct separator insets will be applied by the system. Therefore, you should simply make sure to reload the relevant cells after reordering.
Fix for user reorderings
There's a private method tableView:didEndReorderingRowAtIndexPath: that's called on your table view's delegate after the reordering (animation) has ended. In this method you should reload the cells:
- (void)tableView:(UITableView *)tableView didEndReorderingRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section]
withRowAnimation:UITableViewRowAnimationNone];
}
This fix only works if rows are only moved within a section (and not between sections).
(It would be more efficient to only reload the moved cell and the cell at the old location using the method reloadRowsAtIndexPaths:withRowAnimation:. However, you'd have to store the old location before the cell is moved. which would be more complex to implement. For small sections/simple cells reloading the whole section might be a little bit easier.)
Fix for programmatic moves
Unfortunately, the private delegate method used in the previous fix is only called after reorderings by the user, and not after programmatically moving a row (using moveRowAtIndexPath:toIndexPath:). Therefore, we'll have to use another fix for that.
Since table views internally use CALayer animations, we can use a CATransaction to get to know when the animation has ended. (You do need to add QuartzCore.framework to your project for this.) My fix looks like this:
UITableView+NicelyMoves.h
#import <UIKit/UIKit.h>
#interface UITableView (NicelyMoves)
- (void)nicelyMoveRowAtIndexPath:(NSIndexPath *)indexPath
toIndexPath:(NSIndexPath *)newIndexPath;
#end
UITableView+NicelyMoves.m
#import <QuartzCore/QuartzCore.h>
#import "UITableView+NicelyMoves.h"
#implementation UITableView (NicelyMoves)
- (void)nicelyMoveRowAtIndexPath:(NSIndexPath *)indexPath
toIndexPath:(NSIndexPath *)newIndexPath
{
[CATransaction begin];
[CATransaction setCompletionBlock:^{
// This is executed after the animations have finished
[self reloadRowsAtIndexPaths:#[indexPath, newIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
}];
[self moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
[CATransaction commit];
}
#end
Usage
Now, instead of using moveRowAtIndexPath:toIndexPath:, you should use the custom method nicelyMoveRowAtIndexPath:toIndexPath:. For example, you could do this:
#import "UITableView+NicelyMoves.h"
#implementation YourTableViewController
- (void)moveTheRow
{
// Move row 0 to 2 in section 0
NSIndexPath *from = [NSIndexPath indexPathForRow:0 inSection:0];
NSIndexPath *to = [NSIndexPath indexPathForRow:2 inSection:0];
[self.tableView nicelyMoveRowAtIndexPath:from toIndexPath:to];
}
#end
Please try forcing inset size or setting them to zero in the viewDidLoad to ensure tableView respects them.
This is going to set your tableView separator insets to 30.
- (void)viewDidLoad {
[super viewDidLoad];
if ([self.tableView respondsToSelector:#selector(setSeparatorInset:)]) {
[self.tableView setSeparatorInset:UIEdgeInsetsMake(0, 30, 0, 0)];
}
}
You can also set separator insets only on specific cells as:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCellId"];
if (indexPath.section == 0 && indexPath.row == 0) {
[cell setSeparatorInset:UIEdgeInsetsMake(0, 30, 0, 0)];
} else {
[cell setSeparatorInset:UIEdgeInsetsZero];
}
return cell;
}