Today I made some tests and I am curious of the results. I made an app (ARC) which have UINavigationController and two UIViewControllers. In the first view there is a button and when that button is pressed the second view is loaded. In the second view when shake gesture is detected the first view is loaded and so on. What I notice in instruments is that the heap grows every time when a view is loaded. Here is some code
AppDelegate.m
self.navigationController = [[UINavigationController alloc]init];
self.window setRootViewController:self.navigationController];
FirstViewController *firstview = [FirstViewController alloc]init];
[self.navigationController pushViewController:FirstViewController animated:YES];
FirstViewController.m
-(IBAction)loadSecondView
{
SecondViewController *secondview = [SecondViewController alloc]init];
[self.navigationController pushViewController:secondview animated:YES];
}
SecondViewController.m
-(IBAction)loadFirstView
{
FirstViewController *firstview = [FirstViewController alloc]init];
[self.navigationController pushViewController:first view animated:YES];
}
I can't figure out why that happens. How to avoid heap of growing in that case ?
Actually every time you are creating a new view controller object.. That should not be done.
So every time you allocate a new object and pushed that view, it will be added to the navigation stack and so, the memory grows.
Instead, when you are in first view and tapped the button, you can pop the current view controller and inform the AppDelegate class to show the second view.
Similarly while in second view, when you want to show the first view, pop the current view and inform the AppDelegate class to push the first view controller.
SecondViewController *secondview = [[[SecondViewController alloc]init] autorelease];
FirstViewController *firstview = [[[FirstViewController alloc]init] autorelease];
you should autorelease viewcontrollers (for not ARC)
if second controller opens first, you should do popViewController. If you wont return back, heap will grow
Related
I am properly pushing viewController B from A using navigationController. However, I would like to do it once uiwebview from viewController B finishes its load and not immediately. I tried firstly init B and push A when load ends but with no success, controller is not viewed. How can it be done? thank you.
from controllerA,
self.controllerB = [[BViewController alloc] initWithNibName:#"BViewController" bundle:nil anUser:self.idUser aLang:self.lang];
//[[self navigationController] pushViewController:controllerB animated:NO]; working if pushed directly here
[self.controllerB view];
then, controllerB is initialized, viewDidLoad triggered and when webviewDidFinishLoad, B must be pushed now or viewed at front.
- (void)webViewDidFinishLoad:(UIWebView*)theWebView
{
AViewController *theInstance = [[AViewController alloc] init];
[theInstance pushBcontroller]; }
on AViewController,
-(void)pushBcontroller{
[[self navigationController] pushViewController:self.controllerB animated:NO];
}
not working...
The line AViewController *theInstance = [[AViewController alloc] init]; creates a new instance of a AViewController. Since it's new it isn't part of the view controller hierarchy and is therefore not connected to the navigation controller.
Give your BViewController a reference to the previous controller and use that instead of creating a different one. Or, perhaps better, send a notification when loading is done that the original AViewController uses to know when to change the display.
current version of my project :
I have 5 different UIViewControllers in my app. I've set my
FirstViewController to be the Initial View Controller using the
Attributes Inspector. I move back and forth from one ViewController to
another by using buttons to which I assign modal segues, from one
ViewController to another, using the StoryBoard
What I want to change:
I want to keep the navigation buttons obviously, delete the modal segues and use
a UINavigationController instead. If I understand the concept
correctly, when using a UINavigationController I need to go into each
UIButton-IBAction and at the very end of the method I have to push the next
ViewController I want to move to, onto my NavigationController (do I also
have to pop the current one first?). However, I can't figure out how
to implement all that correctly.
What I've done so far:
I removed all modal segues from the storyboard and kept the navigation buttons along with their corresponding IBActions
I unchecked the box in the Attributes Inspector that was making my FirstViewController the initial View Controller of my app
I went into my AppDelegate.m and tried to create the Navigation Controller there and make my FirstViewController be the RootViewController
MyAppDelegate.m
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIViewController *myFirstViewController = [[FirstViewController alloc] init];
UINavigationController *myNavigationController = [[UINavigationController alloc] initWithRootViewController:myFirstViewController];
[myNavigationController pushViewController:myFirstViewController animated:YES];
// Override point for customization after application launch.
return YES;
}
I then tried to test if the above was working by going into the IBAction of a
navigation button on my FirstViewController and implemented the
following in order to move to my SecondViewController when the
button is pressed :
FirstViewController.m
- (IBAction)goRightButton:(UIButton *)sender
{
// some code drawing the ButtonIsPressed UIImageView on the current View Controller
UIViewController *mySecondViewController = [[SecondViewController alloc] init];
[self.navigationController pushViewController:mySecondViewController animated:YES];
}
but nothing happens. What am I doing wrong ?
You are not linking your XIB file. Please add your navigation controller as
UIViewController *myFirstViewController = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:nil];
navigationController = [[UINavigationController alloc] initWithRootViewController:myFirstViewController];
Use following code to move from one view to another
UIViewController *mySecondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[self.navigationController pushViewController:mySecondViewController animated:YES];
If you are using a storyboard, you should just drag in the navigation controller there and hook it up to your app delegates. As long as it is the main storyboard, and you have identified a view controller to load first, you do not need to load any views in your app delegate.
In order to push a view programmatically that's in a storyboard, you need to do something like the following:
//bundle can be nil if in main bundle, which is default
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
MyCustomViewController *customVC = (MyCustomViewController *)[mainStoryboard instantiateViewControllerWithIdentifier:#"customVC"];
//standard way
[self.navigationController pushViewController:customVC animated:YES];
//custom animation
[UIView transitionWithView:self.navigationController.view duration:0.5 options:UIViewAnimationOptionTransitionCurlUp animations:^{
[self.navigationController pushViewController:customVC animated:NO];
} completion:nil];
You identify the view controller with the identifier you add in the storyboard editor. Below are some screenshots to help show what I mean.
I have some uiview, one call another in this way:
From first uiview:
MyViewController *contr1 = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
[self.view addSubview:contr1.view];
From second uiview:
MyViewController2 *contr2 = [[MyViewController2 alloc] initWithNibName:#"MyViewController2" bundle:nil];
[self.view addSubview:contr2.view];
now in the third uiview i want to return back on the first updating it ( calling viewDidLoad ). how can i do?
First of all - you are doing it wrong.
Since you are using view controllers present them modally or push them:
MyViewController2 *contr2 = [[MyViewController2 alloc] initWithNibName:#"MyViewController2" bundle:nil];
[self presentModalViewController:contr2];
If you want to dismiss modal controllers exactly to your root view controller you should obtain a pointer to it in the controller you are currently using and send it a message to dismiss every modal view that there is on it.
- (IBAction)doHomePage:(id)sender {
MyAppDelegate *appDelegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate;
[appDelegate.navigationController.rootViewController dismissModalViewControllerAnimated:YES];
}
Also instead of viewDidLoad: you might want to use viewWillAppear: or viewDidAppear:.
Sorry beforehand if there are some typo errors in the code since I wrote it by hand.
In my library I have a loading view which pops to a input view. When the user is done with the input view it should go back to the loading view to do some magic again and when done it should show up a third view.
Now, from a usability view I don't want to "slide back" to the loading view, neither do I want to allocate a new loading view when I already have one in memory.
Is there some way I can popToRootViewController while sliding the view forwards?
(Yes, I remove the back button in the loading view)..
Alright here goes - perhaps try using something like this
// This goes in whatever view controller you want to pop with
- (void)popToRootWithForwardAnimation
{
NSMutableArray * viewControllers = [[[self.navigationController viewControllers] mutableCopy] autorelease]
UIViewController * rootViewController = [viewControllers objectAtIndex:0]
[viewControllers removeObjectAtIndex:0]; // try using with and without this line?
[viewControllers addObject:rootViewController];
[self.navigationController setViewControllers:viewControllers animated:YES];
}
// This goes in the root view controller
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated]
NSMutableArray * viewControllers = [[[self.navigationController viewControllers] mutableCopy] autorelease]
if ([viewControllers count] > 1)
{
[viewControllers removeAllObjects];
[viewControllers addObject:self];
[self.navigationController setViewControllers:viewControllers animated:NO];
}
…
…
}
Hmm, I'd say a better approach would be to flip the view in a modal fashion rather than push/pop in a navigation stack. So you would want to do in the input view where you are pushing next view controller:
MagicViewController *magicVC = [[MagicViewController alloc] init];
magicVC.setModalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:magicVC animated:true];
Then when the Magic View controller is done doing its magic, just do at that point (where you otherwise pop):
[self dismissModalViewControllerAnimated:true];
This would be much more cooler than doing simple navigation.
See modal view controllers guide.
I have MainMenuViewController with button which action is
- (IBAction) goToFirstView {
FirstViewController *fvc = [[FirstViewController alloc] init];
[self.view addSubview:fvc.view];
[fvc release];
}
FirstViewController have UIButton with action
- (IBAction) rightArrow {
SecondViewController *svc = [[SecondViewController alloc] init];
[self.view addSubview:svc.view];
[svc release];
}
But when I press "rightArrow" button app crashes with "EXC_BAD_ACCESS". Can't found my problem. Help me please.
[svc release];
The problem is here. When releasing the view controller, the view's events will target a freed object, and make your program crash (probably in viewDidLoad or viewDidAppear if it's instant but it doesn't matter). Note that a view does not (normally, AFAIK) retain it's view controller, if that might have been your assumption...
When you say [self.view addSubview:svc.view] you're adding SecondViewController's view to FirstViewController's view. Similar with MainViewController and FirstViewController. What you'll end up with is a view hierarchy that looks like this:
main view
first view
second view
I doubt that's really what you want. Instead, use a navigation controller with your MainViewController as the nav controller's root controller, and then use -pushViewController:animated: to push the controllers (not the views!) onto the navigation stack.