I am trying to animate a tableview on a view that uses autolayout.
I am able to animate the tableview fine but for some reason there is a unwanted sideways animation when doing so.
The first animation closes the top tableview works fine. However the second animation that extends the bottom tableview is the one that has the sideways movement. Here is my code:
[self.dataSelectionTable layoutIfNeeded];
[self.comparatorSelectionTable layoutIfNeeded];
[UIView animateWithDuration:0.5 animations:^{
self.dataSelectionTableHeight.constant = 44;
[self.view layoutIfNeeded]; and then captures all of the frame changes
} completion:^(BOOL finished){
[UIView animateWithDuration:0.5 animations:^{
self.comparatorSelectionTableHeight.constant = 200;
[self.view layoutIfNeeded];
}];
}];
I have tried changing the animation type but that does not seem to work.
Here is a link to a gif that shows the unwanted sideways animation: http://makeagif.com/YubmP2
Edit
Here is the code for populating the tableviews:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
cell.textLabel.numberOfLines = 0;
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:13.0];
}
if (tableView == self.dataSelectionTable) {
cell.textLabel.text = self.dataSelectionTitles[indexPath.row];
}
else {
cell.textLabel.text = [self.comparatorList[indexPath.row] name];
}
return cell;
}
I tried to duplicate your problem, but got slightly different results. Instead of a sideways movement, I got a downward movement of the label within the cell (but only the first time I did the animation).
I noticed that if I set the table view's height constraint's constant value to 0 in viewDidLoad, cellForRowAtIndexPath was not called until I did the expansion, so I'm guessing that some of the laying out of views of the table view wasn't happening until then.
I fixed the problem by setting the alpha value of the table view to 0 in IB, and set its height to 400 (the value I wanted after expansion). Then in viewDidAppear, I did this,
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.heightCon.constant = 0;
self.tableView.alpha = 1;
}
This caused the table's data source methods to run right away, but with the table view not visible. Now when I animated the expansion, I didn't get any movement of the label within the cell.
Related
Hope someone can help as I've hit a bit of a roadblock. I started off with a UIScrollview that would have content with dynamic height switch in and out. After a few issues with AutoLayout I have switched to using a UITableView instead.
At the minute I have a UITableView in the main UIView and then separate UITableViewCells within storyboard. Each UITableViewCell is connected via an IBOutlet.
Now to switch the content I use the following on reloadData
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(contentToShow == 1){
UITableViewCell *cell = self.cellHome;
self.viewIcon1.layer.cornerRadius = 40;
self.viewIcon1.layer.masksToBounds = true;
self.viewIcon2.layer.cornerRadius = 40;
self.viewIcon2.layer.masksToBounds = true;
self.viewIcon3.layer.cornerRadius = 40;
self.viewIcon3.layer.masksToBounds = true;
self.viewIcon4.layer.cornerRadius = 40;
self.viewIcon4.layer.masksToBounds = true;
[self addUnderline:self.imageViewTitle];
[self addUnderline:self.imageViewTitle2];
[self addUnderline:self.imageViewTitle3];
return cell;
}else {
UITableViewCell *cell = self.cellCustInfo;
[self addUnderline:self.imageViewTitle4];
return cell;
}
}
Now this seems to change the content of the UITableView but I am having strange issues with the position of the scroll immediately after the change. I am using the following to try and get back to the top of the UITableView.
- (IBAction)showFAQScreen {
contentToShow = 2;
[UIView animateWithDuration:0.4 animations:^{
self.tableViewContent.contentOffset = CGPointZero;}];
[self.tableViewContent reloadData];
[self showMenu:nil];
}
If the scroll hasn't moved then the content is displayed correct, if it has slightly moved then the content scrolls as expected. However if the scroll has moved down significantly the scroll jumps to the bottom and it is not until I touch the scroll that it corrects itself. Any ideas what is going on and suggestions on another approach to take?
Thanks
Can you try
UIView animateWithDuration:duration animations:^{
self.tableViewContent.contentOffset = CGPointZero;
} completion:^(BOOL finished) {
[self.tableViewContent reloadData];
[self showMenu:nil];
}];
I got a UIView inside a UITableViewCell (dynamic prototype, not sure if it's important to clarify that) with a background color and changed the view's frame with the following code (inside cellForRowAtIndexPath method):
UIView* verde = (UIView*) [cell viewWithTag:202];
verde.frame =CGRectMake(20, 30, x, y);
The problem is that when the UITableView is drawn for the first time (when the screen loads) the UIView has the original size (established by default on the original prototype from the storyboard). But when I scroll down, the cell leaves the screen, therefore reused by another cell. When scrolling back to the cell, cellForRowAtIndexPath is called again and now the frame has the correct size.
I've tried calling [verde setNeedsDisplay]; after changing the frame without success.
Disabling autolayout solved the issue as pointed out by Timothy Moose. Somehow the cells in the first draw (screen first load) retain the layout specified in the storyboard, when they leave screen and they are reused or created again the views are finally drawn with the correct frame.
Try this , Hope you can resolve the issue
-(UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [theTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
}
else{
[[cell.contentView viewWithTag: 202] removeFromSuperview];
}
UIView *view =[[UIView alloc]init];
view.frame = cell.contentView.frame;
view.tag = 202;
view.backgroundColor = [UIColor redColor];//To be sure that the custom view in the cell
[cell addSubview:view];
return cell;
}
I have a UITableView with multiple UILabels. The issue is that the text in these cells change dynamically as I receive data from the server. It works fine when I load the view controller. But as I scroll, the height of the cells are not updated as heightForRowAtIndexPath is only called once.
Here are the screenshots:
As I've shown in the screenshot, the question label reduces in size which leads to a gap (shown by arrow).
Here's my cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIndentifier = #"CustomCell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndentifier];
if (cell == nil)
{
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIndentifier];
}
cell.question.autoDetectLinks = YES;
// Used to populate cell from NSDictionary
[self setDataToCell:cell AtIndexPath:indexPath];
return cell;
}
Here's my custom cell's layoutSubviews:
- (void) layoutSubviews
{
CGRect frame = self.question.frame;
frame.size.width = 277.0f; //you need to adjust this value
self.question.frame = frame;
self.question.numberOfLines = 2;
[self.question sizeToFit];
// Place time below question
CGRect timeFrame = self.time.frame;
timeFrame.origin.y = self.question.frame.origin.y + self.question.frame.size.height + 5;
self.time.frame = timeFrame;
[self.time sizeToFit];
}
So to tackle this situation I called
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[_tableIndexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
in - (void) scrollViewDidScroll:(UIScrollView *)scrollView
This solves my problem but reduces the performance and the elements jump around before settling even after setting the animation as UITableViewRowAnimationNone. Is there a better way of doing it? Should I call reloadRowsAtIndexPaths somewhere else?
Thanks.
Ok here come the edited answer:
The problem is your sizeToFit in your layoutSubviews. Just follow this link to resolve your issue:
here
If you also want the cell and the label to dynamically resize to their corresponding text but at the same time your timelabel to be directly underneath it you will have to determine the size of your uilabel based on the text, font and font-size.
See here for more information:
here
I want to animate my collectionview cell so that when I touch it, a mapview slides out from underneath it and basically expands into the space, by pushing the cell underneath it down a little bit. I tried using this: UICollectionView: Animate cell size change on selection, but was unable to get it to work properly.
Here what I tried.
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
[((FeedCell *)[collectionView cellForItemAtIndexPath:indexPath]) setIsSelected:YES];
[collectionView.collectionViewLayout invalidateLayout];
__weak FeedCell *cell = (FeedCell *)[self.photoCollectionView cellForItemAtIndexPath:indexPath]; // Avoid retain cycles
void (^animateChangeWidth)() = ^()
{
CGRect frame = cell.frame;
frame.size = CGSizeMake(frame.size.width, 420);
cell.frame = frame;
};
// Animate
[UIView transitionWithView:[collectionView cellForItemAtIndexPath:indexPath] duration:0.5f options: UIViewAnimationOptionCurveLinear animations:animateChangeWidth completion:nil];
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
if(((FeedCell *)[self.photoCollectionView cellForItemAtIndexPath:indexPath]).isSelected == YES){
return CGSizeMake(320, 420);
}
return CGSizeMake(320, 320);
}
I have a custom CollectionView cell and it has a property for isSelected. I'm not sure what I should do for the xib of the cell (whether to put the map view in there, have the CGSize be the selected size or deselected size, etc.) Any help would really be appreciated!
I am trying to do the same thing as you and found your question in my search. It appears that you have a pretty good solution (at least in my inexperienced opinion). What isn't working for you? I got it to do what I wanted by copying your code almost verbatim. I changed a few things, but didn't test the code before making the changes. Here is what I have that works for me in my didSelectItemAtIndexPath method:
__weak MyCell *cell = (MyCell*)[collectionView cellForItemAtIndexPath:indexPath];
[cell setSelected:YES];
[collectionView.collectionViewLayout invalidateLayout];
void (^animateChangeWidth)() = ^()
{
CGRect frame = cell.frame;
frame.size = CGSizeMake(frame.size.width, 340.0f);
cell.frame = frame;
};
// Animate
[UIView transitionWithView:cell duration:0.5f options: UIViewAnimationOptionCurveEaseInOut animations:animateChangeWidth completion:nil];
I also have overridden collectionView:layout:sizeForItemAtIndexPath so I assume that is working for you as well.
Sorry I can't be more specific. It would help if you said what about it wasn't working.
I have a custom UITableViewCell with three labels - title, subtitle and right detail.
Now when a User taps on Cell this Cell becomes checked with the normal Checkmark, but I need to animate the right label to the left. I thought I could just do
CGRect rect = cell.playerRatingLabel.frame;
rect.origin.x -= 10;
[CLCPlayerViewCell animateWithDuration:1.0 animations:^{
cell.playerRatingLabel.frame = rect;
}];
but this appears to do nothing at all. I think it's about constraints but I don't know how to handle this, I'm using auto Layout.
Thanks for any help
Your playerRatingLabel should have a constraint to the right edge of the cell. Your custom cell need to make an IBOutlet to that constraint. Then on the cell tap, animate the constant parameter of that constraint (I've called the outlet rightCon in the example):
[UIView animateWithDuration:1.0 animations:^{
cell.rightCon.constant = 30; // change this value to meet your needs
[cell layoutIfNeeded];
}];
Here is the complete implementation that I use to do this. My custom cell had two labels, and I animate the right one when you click a cell and add a checkmark. I created a property selectedPaths (a mutable array) to keep track of the cells that are checked. If you click on a cell that's already checked, it un-checks it, and animates the label back to its original position.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
RDCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.leftLabel.text = self.theData[indexPath.row];
cell.accessoryType = ([self.selectedPaths containsObject:indexPath])? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
cell.rightCon.constant = ([self.selectedPaths containsObject:indexPath])? 40 : 8;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
RDCell *cell = (RDCell *)[tableView cellForRowAtIndexPath:indexPath];
if (! [self.selectedPaths containsObject:indexPath]) {
[self.selectedPaths addObject:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[UIView animateWithDuration:.3 animations:^{
cell.rightCon.constant = 40;
[cell layoutIfNeeded];
}];
}else{
[self.selectedPaths removeObject:indexPath];
cell.accessoryType = UITableViewCellAccessoryNone;
[UIView animateWithDuration:.3 animations:^{
cell.rightCon.constant = 8;
[cell layoutIfNeeded];
}];
}
}
You're trying to adjust the cell's frame which won't have any effect. Try adjusting the frame of the cell's contentView.
i guess following function will solve your problem
pass your label as cell.textLabel.layer and den pass the point like
CGPointMake(0, self.view.center.y)
-(void)moveLayer:(CALayer*)layer to:(CGPoint)point
{
// Prepare the animation from the current position to the new position
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
animation.fromValue = [layer valueForKey:#"position"];
animation.duration = 0.2;
animation.toValue = [NSValue valueWithCGPoint:point];
// Update the layer's position so that the layer doesn't snap back when the animation completes.
layer.position = point;
// Add the animation, overriding the implicit animation.
[layer addAnimation:animation forKey:#"position"];
}