My code crashes with a bad access, when i have a view with tables. I have figured what the problem is, but looking for the right solution.
In View 1 - i have a button that creates and instance of view 2 and then releases it, something like this:
settingsScreen *settings = [[settingsScreen alloc] initWithNibName:#"settingsScreen" bundle:nil];
CGRect theFrame = settings.view.frame;
//theFrame.origin = CGPointMake(self.view.frame.size.width, 0);
theFrame.origin = CGPointMake(0,-(self.view.frame.size.height));
settings.view.frame = theFrame;
theFrame.origin = CGPointMake(0,0);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.8f];
settings.view.frame = theFrame;
[UIView commitAnimations];
[self.view addSubview:settings.view];
[settings release];
In view 2 there is table, where i am setting the datasource and delegate to self on viewDidLoad, but this crashes the app with exec_bad_access
If i remove [settings release] in View 1, everything is fine.
If i do not release the view, is'nt it wrong to leave it in the memory?
how can i get over this situation?
Thanks
you should use either pushViewController or presentModalViewController instead of adding a view of one Viewcontroller in another Viewcontroller and when you want to come back you can use popViewController or dismissModalViewController, this helps in memory management as well. Also use [NSBundle mainBundle] for bundle parameter in initWithNibName which finds proper xib in the bundle.
I am sure somehow your settings instance is being released somewhere in your code again and it makes your app unstable.
Sanniv's answer is correct. You should use push-pop concept of navigation controller or present the controller in a modal way.
(Visit: i) http://forums.macrumors.com/showthread.php?t=472236 & ii) http://www.iphonedevsdk.com/forum/iphone-sdk-development/3643-pushviewcontroller-versus-presentmodalviewcontroller.html for more info).
Now the error you're getting is because of your "settings" object is being used in animation which you're releasing before your animation gets finished. Thus, instead of releasing the object immediately, release in another method which would be called exactly after animation stops. Like following way:
settingsScreen *settings = [[settingsScreen alloc] initWithNibName:#"settingsScreen" bundle:nil];
CGRect theFrame = settings.view.frame;
//theFrame.origin = CGPointMake(self.view.frame.size.width, 0);
theFrame.origin = CGPointMake(0,-(self.view.frame.size.height));
settings.view.frame = theFrame;
theFrame.origin = CGPointMake(0,0);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.8f];
[UIView setAnimationDidStopSelector:#selector(releaseObjects)];
settings.view.frame = theFrame;
[self.view addSubview:settings.view];
[UIView commitAnimations];
In the "releaseObjects" objects method, you should release your settings object. And in a case, if your settings object is a local, do it autorelease like this way -
settingsScreen *settings = [[[settingsScreen alloc] initWithNibName:#"settingsScreen" bundle:nil] autorelease];
Related
I'm calling a method from another view controller by using this:
InitialViewController *secondController = [[InitialViewController alloc] init];
[secondController forecast];
Here's the method in the InitialViewController:
-(void)forecast{
[UIView beginAnimations:#"Forecast" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:1.0f];
self.customPager.frame = CGRectMake(0,5, 320, 510);
self.view1.frame = CGRectMake(-320,5, 320, 510);
radarView.frame = CGRectMake(0,560, 320, 510);
[UIView commitAnimations];
NSLog(#"Method Passed");
}
In my console, I get the NSLog "Method Passed", but the UIView animation does not occur at all. Any ideas?
It seems to me that you are not displaying in any way the view associated to secondController. I.e., after doing:
InitialViewController *secondController = [[InitialViewController alloc] init];
I would expect you do something like:
[self.view addSubview:secondController.view];
This would trigger loadView/viewDidLoad before you call forecast. Furthermore, I would give a chance to the UI to be show your view before animating it; thus, I would call forecast either like this:
[self performSelector:#selector(forecast) withObject:nil afterDelay:0.0];
or from viewDidAppear.
EDIT:
According to your comment,
your InitialViewController is already displayed on screen. In this case, what you should do is getting a reference to it and sending it the forecast message.
What you are doing now is instantiating a new InitialViewController (and then sending the forecast message to it) that has no relation with the one already displayed.
Even though you instantiate secondController, its view is never displayed on screen, or even loaded, so any animations that you apply to it have no effect.
If your second controller is already on the screen, then your alloc init is creating another one, not getting the one already present (the log works because you are creating an instance of InitialViewController, so its code will run, but another instance's view is the one you're seeing on screen). You need to get a reference to the one on screen. I can't say how you should do that without knowing how you got your 2 controllers on the screen in the first place.
I am using ARC in my app.
Now I faced a real problem. I need to animate two view controllers from left to right. So I just animate the the two views in button action like,
// thanks to stackoverflow for this code
test *control = [[test alloc] initWithNibName: #"test" bundle: nil];
control.view.frame = self.view.frame;
control.view.center = CGPointMake(self.view.center.x + CGRectGetWidth(self.view.frame), self.view.center.y);
[self.view.superview addSubview: control.view];
// Animate the push
[UIView beginAnimations: nil context: NULL];
[UIView setAnimationDelegate: self];
[UIView setAnimationDidStopSelector: #selector(pushAnimationDidStop:finished:context:)];
control.view.center = self.view.center;
self.view.center = CGPointMake(self.view.center.x - CGRectGetWidth(self.view.frame), self.view.center.y);
[UIView commitAnimations];
- (void) pushAnimationDidStop: (NSString *) animationID finished: (NSNumber *) finished context: (void *) context
{
[self.view removeFromSuperview];
}
and in the second view controller I did the same but change the animation direction.
My real problem is when I run this code, every time I create an object of the another class and it is not deallocating. By profiling this code, found that the every time the object is alive and it is not dying and the memory allocation get increasing.
You can run the allocation instrument with reference count recording enabled. Then evaluate where the offset differs from your expectations, based on that data.
following situation.
i have an app that uses a UINavigationController for navigating.
For a push of a special Navigation Controller i want a custom Animation, a zoom out.
What i have is looking good so far, the only problem is, that the "old" Viewcontroller disappears before the animation starts, so the new viewcontroller zooms out of "nothing" instead of viewing the old viewcontroller in the background.
For better viewing i created a simple sample app: download
Does anybody know, how to animate the new viewcontroller (image3) that the old view controller(image1) stays in the background while animating (image 2)?
/* THIS CANNOT BE CHANGED */
AnimatedViewController *animatedViewController = [[AnimatedViewController alloc] initWithNibName:#"AnimatedViewController" bundle:nil];
/* THIS CAN BE CHANGED */
animatedViewController.view.transform = CGAffineTransformMakeScale(0.01, 0.01);
[UIView beginAnimations:#"animationExpand" context:NULL];
[UIView setAnimationDuration:0.6f];
animatedViewController.view.transform=CGAffineTransformMakeScale(1, 1);
[UIView setAnimationDelegate:self];
[UIView commitAnimations];
/* THIS CANNOT BE CHANGED */
[self.navigationController pushViewController:animatedViewController animated:NO];
Additional information: my app isn't that simple. i use three20 framework for my app but the pushing and creating of the view controllers is simply what three20 does. i only can hook into the part between (THIS CAN BE CHANGED in my code). i cannot change the code before and after this (except with a lot of researching).
I whipped this up very quickly. The idea is to call pushViewController after the scale animation is completed.
-(IBAction)animateButtonClicked:(id)sender {
animatedViewController = [[AnimatedViewController alloc] initWithNibName:#"AnimatedViewController" bundle:nil];
animatedViewController.view.transform = CGAffineTransformMakeScale(0.01, 0.01);
[UIView animateWithDuration:0.6
animations:^{
[self.view addSubview:animatedViewController.view];
animatedViewController.view.transform=CGAffineTransformMakeScale(1, 1);
}
completion:^(BOOL finished){
[animatedViewController.view removeFromSuperview];
[self.navigationController pushViewController:animatedViewController animated:NO];
}];
}
Ok, one way to do this (a bit ugly) is to do the animation and upon the completion of the animation, do the pushViewController. When you do the animation, you need to animate up what the about-to-be-presented view controller's screen would look like. Since the animation ends with the new view, when the new view comes to the screen, nothing should change because its the same as the just animated view.
I have a method like the following:
- (void)add:(id)sender {
MyAddViewController *controller = nil;
controller = [[MyAddViewController alloc] initWithAddType:1];
//controller.delegate = self;
// Do some animation. Slide it in from the right side of the screen.
CGPoint initialCenter = self.view.center;
controller.view.center =
CGPointMake(initialCenter.x + 320, initialCenter.y);
// Add the subview now with it's center + 320 off the screen.
[self.view addSubview:controller.view];
[UIView beginAnimations:#"animation" context:NULL];
[UIView setAnimationDuration:0.35];
[UIView setAnimationDelegate:self];
controller.view.center = CGPointMake(initialCenter.x, initialCenter.y);
//[UIView setAnimationDidStopSelector:#selector(aMethodToBeCalledAfterAnimationEnd:finished:context:)];
[UIView commitAnimations];
//[controller release];
}
You see I have a controller release as the last line in my add method. If I uncomment this line the animation completely dies and it just dumps the view into place with no animation.
Is there a better way to do release without having to create another method which does cleanup via [UIView setAnimationDidStopSelect:#selector(aMethodToBeCalledAfterAnmiationEnd... ?
Or can I do something like setAnimationDidStopSelector:#selector([controller release]) ? :)
Thanks in advance!
Using setAnimationDidStopSelector: is the proper way to solve the generic problem of releasing resources after an animation completes.
Take a step back and reconsider what you're doing here, though. Why are you looking to free the controller you just created? If you are only creating the controller for the purpose of getting at the view, that isn't the right way to do it. Build yourself a factory method to create your view, or use the methods in NSBundle to load the view from a NIB.
You can do this:
[UIView setAnimationDelegate: controller];
[UIView setAnimationDidStopSelector:#selector(release)];
I see you tagged this iphone-sdk-4.0. You could use the new block-based animations to do this and any other cleanup. See the documentation for details.
Im using some transitions(Flip) to switch to my "settings/about" view in my iPhone app.
I initiate the transition like this:
SettingsAboutViewController *settingsView = [[SettingsAboutViewController alloc] init];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight
forView:[self.navigationController view ]
cache:YES];
[self.navigationController.view addSubview:settingsView.view];
[UIView commitAnimations];
In my SettingsAboutViewController's viewDidLoad method I set up all related graphics using setFrame:CGRectMake(x, y, w, h), there is no Nib involved.
My problem is that all the graphics seems to be positioned in 0,0 and only when the flip is 90% done does it "snap" into place.
I tried calling [super viewDidLoad] after the setup and before, I tried placing my setup code inside loadView instead, but all has failed so far.
It seems the view is not all build before I call the animation. I never had problems like this before and this particular view is not graphics heavy and demanding in any other way?
What is it I need to take into account?
Thanks.
How about using presentModalViewController?? Its simpler and easy to use..
SettingsAboutViewController *settingsView = [[SettingsAboutViewController alloc] initWithNibName:#"SettingsAboutViewController " bundle:nil];
settingsView.delegate = self;
// The magic statement. This will flip from right to left. // present the modal view controller then when you dismissModalViewController.
// it will transition flip from left to right. Simple and elegant.
settingsView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:settingsView animated:YES];
[settingsView release];
I think that would solve your problem.
Good Luck..!