UITableView frame height animation glitch - objective-c

If I attempt to animate the frame height of a tableView (ex: height -= 200), the cells that appear in the last 200px disappear suddenly before the smooth animation of the frame completes.
To make sure that it's nothing else I'm doing, I created a new View-Based application. In the main viewController I create my own tableview with enough pseudo rows to fill the entire screen. And on selection of a row I do a simple height animation.
most relevant code:
- (void)viewDidLoad {
[super viewDidLoad];
self.myTable = [[[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)] autorelease];
myTable.delegate = self;
myTable.dataSource = self;
[self.view addSubview:myTable];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
CGRect frame = self.myTable.frame;
frame.size.height = 200;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDelay:.5f];
[UIView setAnimationDuration:0.5f];
self.myTable.frame = frame;
[UIView commitAnimations];
}
Does anyone know why this is happening, or what a fix/workaround may be?
Any suggests are really appreciated.
TIA!

I'm not sure I had exactly the same problem, but using [UIView setAnimationBeginsFromCurrentState:YES]; solved (parts of) the glitches (my table view slid around crazily when animating a frame height change).

Same problem here as well. This is what I'm doing and the origin animates smoothly, but the size changes immediately... annoying.
[UIView beginAnimations:#"HideTabbar" context:nil];
[UIView setAnimationDuration:.3];
self.tableView.frame = CGRectMake(0.0, 44.0, 320, 366);
[UIView commitAnimations];
UPDATE: Try adding this before the animation:
self.tableView.autoresizingMask = UIViewAutoresizingNone;

For anyone hitting this question & answer in the future, here's how to use #ryyst's answer in UIView's block based animations introduced in iOS 4.
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
// change frame etc here
} completion:^(BOOL finished) {
// any clean up here
}];

This is an old question and there are already a couple suggestions for a workaround, but I thought I'd add mine.
I ended up animating the contentInset and scrollIndicatorInsets properties, which provides the illusion that the table itself is being resized.

I have exactly the same problem. I imagine that tableviews have a special behavior on "setFrame:", it seems that the tableview remove the cells that won't be visible with the new frame.
In case of an animation, the cells won't be visible only at the end of the animation, but it seems that tableviews don't care.
If someone have a better theory, I'd be glad to hear it !

Finally found the solution! there is indeed a bug!! don't use variables, when animating the height use [[UIScreen mainScreen] bounds]. Like this:
[UIView animateWithDuration:0.5 animations:^{
tableView.frame=CGRectMake(0, 38, fullScreenRect.size.width, [[UIScreen mainScreen] bounds].size.height-38);
}];
not :
[UIView animateWithDuration:0.5 animations:^{
tableView.frame=CGRectMake(0, 38, fullScreenRect.size.width, fullScreenRect.size.width-38);
}];
and it will work like magic!

I found your question while seeking the proper method to resize a tableView. I think your problem is in your animation you've specified UIView instead of UITableView. I was unable to duplicate your problem using either UIView or UITableView, but I'm using SDK 3.1 and it might be a bug that has been fixed since your post. I'm not sure, but I hope this helps!

It's an old question but this might help someone in the future;
I solved a similar problem by embedding the tableview in a UIView, and resizing the UIView instead of tableview. I set the tableview's height to a large value and also "clip subviews" property on the UIView. I resize the UIView proportional to tableview's contentSize. Its not a good solution but it worked for my purposes.

Related

UIView not animating constraint change

I have a question regarding the UIView method:
+ (void)animateWithDuration:(NSTimeInterval)duration
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion
Basically I am just trying to set a height constraints constant to 0 and have the change look like its slowly shrinking...
My Code looks like this:
[UIView animateWithDuration:0.7
animations:^{
//hide the Title
self.titleCellHeigthConstraint.constant = kLabelHeightZero;
[self.contentView layoutIfNeeded];
}
completion:nil];
Unfortunately it's not animating. It simply makes all changes appear with a little delay (which might be the 0.7 seconds specified in the method).
Now my question is if there is any way to get the UIView to animate the change?
Thanks in Advance!
Edit:
The weird thing is if I reverse the changes in an animated fashion it does it exactly the way I want it. The code I'am using for that is:
//animate layout changes
[UIView animateWithDuration:0.7
animations:^{
//hide activity indicator and label
[self.activityIndicator stopAnimating];
[self.label setHidden:YES];
}
completion:^(BOOL arg0){
[UIView animateWithDuration:0.3
animations:^{
//show title label
self.titleCellHeigthConstraint.constant = kDefaultLabelHeight;
[self.contentView layoutIfNeeded];
}];
}];
You want to change the properties of the UIView not the NSLayoutConstraint in the animation method. I.e. Change the layout constraint's constant before the UIView method and then change the UIView's frame property in the animation method.
This way you won't need to call layoutIfNeeded.

Animating with Auto Layout and iOS7 jumps to end of animation (fine in iOS6)

I am using Autolayout and animating by changing the constraints, however, in iOS7 the view simply jumps to the end position - in iOS6 I get a nice animation.
Is should be noted these views are UICollectionViews and I have checked the Storyboard and there are no Layout errors.
All I can think is there is something and am or am not setting on the Storyboard or something that I am doing wrong with the Constant settings in the Storyboard.
primaryMenuYContraints.constant = BUTTOMX;;
leftMenuYContraints.constant = 136.0f;
leftMenuBottomConstraint.constant = 5.0f;
[UIView animateWithDuration:0.7f
delay:0.0f
options:UIViewAnimationOptionCurveLinear
animations:^
{
// Move in menus
[self.primaryOptionCollection layoutIfNeeded];
[self.menuOptionCollection layoutIfNeeded];
}
completion:^(BOOL finished)
{
}];
I changed to and now works in both iOS7 and 6, still not sure why it does/did it though! I still think I am setting something up wrong in the Storyboard. I am add another view (nothing to do with this lot) programmatically so I believe that is based around frames until I convert it (which I am not doing).
primaryMenuYContraints.constant = BUTTOMX;;
leftMenuYContraints.constant = 136.0f;
leftMenuBottomConstraint.constant = 5.0f;
[UIView animateWithDuration:0.7f
delay:0.0f
options:UIViewAnimationOptionCurveLinear
animations:^
{
// Move in menus
[self.view layoutIfNeeded];
}
completion:^(BOOL finished)
{
}];

Alternative to "self" in calling methods in Objective-C

This may sound really noob, but i've spent an entire day wrestling with this problem and would appreciate some help.
You see i have a method which I call more than once inside gameplay. If I use [self myMethod]; then it works for ONE time. And then when I call it again, the animation in the method doesn't commence anymore.
What I need is to replace "self" with an alternative that can be "alloc'ed" and "released" to make my animations work.
I've tried;
#implementation gameViewController
gameViewController *object = [[gameViewController alloc] init];
[object myMethod];
However the above substitute for self doesn't even call on the method. I don't know what I did wrong, it's suppose to work just like "self".
Is there something i missed? How do you make an object of the class to work just like "self" does?
Thanks so much.
Here is a more detailed look of my code;
[self explosionAnimations];
- (void) explosionAnimations
{
UIImage *image = [UIImage imageNamed: #"Yellow Explosion.png"];
[bomb setImage:image];
[UIView beginAnimations:#"bomb1ExplosionIncrease" context:NULL];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelegate:self];
bomb.transform = CGAffineTransformMakeScale(3.4, 3.4);
[UIView commitAnimations];
}
The setImage works fine every time. But the animations stop working on the second call of the method. While in the console it logs "animation completed" with nothing happening to the image.
This leads me to believe that somehow "self" believes that the animation was already done and will not bother to do it again. So I thought a new "alloc" might give it a kick awake.
The problem doesn't have anything to do with "self". The problem is that you set the transform in your animation, and then when you run it again, you're setting the same transform, so it does nothing. You need to reset the frame to the new frame and then set the transform back to the identity transform before you do the animation again. Also, you should be using block based animations. I'm not sure this is the best way to do it, but this worked for me (if you have auto layout turned off).
- (void)explosionAnimations {
UIImage *image = [UIImage imageNamed: #"Yellow Explosion.png"];
[self.bomb setImage:image];
[UIView animateWithDuration:.5 animations:^{
self.bomb.transform = CGAffineTransformMakeScale(3.4, 3.4);
} completion:^(BOOL finished) {
CGRect newFrame = self.bomb.frame;
self.bomb.transform = CGAffineTransformIdentity;
self.bomb.frame = newFrame;
}];
}
If you're doing this in an app with auto layout turned on (which it is by default), then I would not use a transform, but just resize the width and height of the image view by adjusting its height and width constraints. So, in this method, you should make IBOutlets to height and width constraints you make in IB, then change their constant values in an animation block:
[UIView animateWithDuration:.5 animations:^{
self.heightCon.constant = self.heightCon.constant * 3.4;
self.widthCon.constant = self.widthCon.constant * 3.4;
[self.view layoutIfNeeded];
}];

Resize UITableView when I slide up a UIView, just like how it happens when the keyboard is shown

I am sliding up a UIView which has a UIDatePicker as a subview. This is added above my UITableView, but unfortunately some of the tableView rows are still under my UIView.
UITableView is pushed up with keyboard:
UITableView is NOT pushed up with my view, covers up last few fields:
Is it possible to resize the UITableView dynamically when I slide up my view, just like when the keyboard is shown when in a tableView and all the rows are still able to be seen?
EDIT:
Just found a great Apple example: http://developer.apple.com/library/ios/#samplecode/DateCell/Introduction/Intro.html
Yes, it is possible. You could just go ahead and change the size of the UITableView dynamically as part of the animation that slides up the view. If you want to do what the UITableView actually does in response to the keyboard, leave its size alone and instead change its content inset and scroll indicator insets. See my answer here and the examples linked to:
uitableview not resizing with keyboard
Ok, it was easier than I thought. In my case, when I touch a row, I want the picker to show and the tableView to change its height, so I use:
[UIView animateWithDuration:0.4 delay:0.0 options: UIViewAnimationCurveEaseOut animations:^{
self.pickerView.frame = CGRectMake(0, self.view.bounds.size.height-200, self.view.bounds.size.width, self.pickerView.frame.size.height);
// shrink the table vertical size to make room for the date picker
CGRect newFrame = self.tableView.frame;
newFrame.size.height -= self.pickerView.frame.size.height;
self.tableView.frame = newFrame;
} completion:nil];
Then when I click the done button, and want to return the tableView back to the full height and hide my datePicker, I use:
- (void)doneWithPicker:(BOOL)remove {
CGRect newFrame = self.tableView.frame;
newFrame.size.height += self.pickerView.frame.size.height;
self.tableView.frame = newFrame;
[UIView animateWithDuration:0.4 delay:0.0 options: UIViewAnimationCurveEaseOut animations:^{
self.pickerView.frame = CGRectMake(0, self.view.bounds.size.height+300, self.view.bounds.size.width, self.pickerView.frame.size.height);
} completion:^(BOOL finished){
if (remove) {
[self.pickerView removeFromSuperview];
self.pickerView = nil;
}
}];
}
Also, when adding the pickerView as a subview, make sure to add it to the window:
[self.view.window addSubview:self.pickerView];

Fading UIView allows subviews to be seen

I have a UIScrollView which contains various subviews (UIImageViews, UILabels and standard UIViews). Some of the UIImageViews are partially covered by other UIViews.
However, when I fade out the UIScrollView, the partially covered parts of the UIImageViews are being exposed for the brief moment of the animation.
I want to be able to fade the scrollview and all it's contents at the same time in the same animation - i.e. not revealing any of the partially covered images.
If it's not possible, I can always add a UIView on top of all the other controls and fade it from alpha 0 upto 1 to hide everything, but I'm sure there's a way to perform a complete fade on a view and all it's subviews.
I tried this:
[UIView beginAnimations:nil context:NULL];
[scrollViewResults setAlpha:0.0f];
[UIView commitAnimations];
And I've tried this:
- (IBAction)questionGroupChanged:(UIButton*)sender {
[UIView beginAnimations:nil context:NULL];
[self fadeViewHierarchy:scrollViewResults toAlpha:0.0f];
[UIView commitAnimations];
}
- (void)fadeViewHierarchy:(UIView*)parentView toAlpha:(float)alpha {
[parentView setAlpha:alpha];
for (UIView *subView in parentView.subviews) {
[self fadeViewHierarchy:subView toAlpha:alpha];
}
}
But I've still not cracked it. Any ideas?
This happens because of the way the compositor works. You need to enable rasterization on the view's layer when fading it in/out:
view.layer.shouldRasterize = YES;
You should probably only enable this for the duration of the animation because it will take up some extra memory and graphics processing time.
Mike's answer is the correct one and he deserves all credit for this. Just to illustrate, it might look like:
- (void)fadeView:(UIView*)view toAlpha:(CGFloat)alpha
{
view.layer.shouldRasterize = YES;
[UIView animateWithDuration:0.75
animations:^{
view.alpha = alpha;
}
completion:^(BOOL finished){
view.layer.shouldRasterize = NO;
}];
}
Thus, using your scrollViewResults, it would be invoked as:
[self fadeView:scrollViewResults toAlpha:0.0f];
Did you try with UIView class methods +animateWithDuration:* (available on iOS 4 and +)
Like :
- (void)fadeAllViews
{
[UIView animateWithDuration:2
animations:^{
for (UIView *view in allViewsToFade)
view.alpha = 0.0;
}
completion:^(BOOL finished){}
];
}