I have a UINavigationController in which I push several UIViewControllers.
I want that every time a new UIViewController is pushed, the older ones get released from memory.
For that, in every UIViewController I'm putting this piece of code:
-(void)viewDidAppear{
self.navigationController.viewControllers = #[self];
}
This way the viewControllers array gets reduced only to the one being displayed. But since I'm using ARC, every UIViewController is a strong reference and it's not being released from memory.
I've tried creating a weak instance of every UIViewcontroller when pushing them using this code:
FirstViewController.m
-(IBAction)goToSecond:(id)sender{
SecondViewController *secondVC = [[SecondViewController alloc]init];
__weak SecondViewController *weakSecondVC = secondVC;
[self.navigationController pushViewController:weakSecondVC animated:NO];
}
But this way I'm creating two instances: the weak one that is being pushed and the strong one that stays in memory.
I have also tried creating just the weak reference and pushing it:
FirstViewController.m
-(IBAction)goToSecond:(id)sender{
__weak SecondViewController *weakSecondVC;
[self.navigationController pushViewController:weakSecondVC animated:NO];
}
But then I get the following:
Application tried to push a nil view controller on target <UINavigationController: 0x127606210>.
Is there any way to achive this?
EDIT:
As suggested in the answer I've tried doing the following:
-(void)goToSecond:(id)sender{
SecondViewController *pistasVC = [[EYSPistasViewController alloc] init];
[self.navigationController setViewControllers: #[secondVC]];
[self.navigationController popViewControllerAnimated:NO];
}
The UINavigationController stack of UIViewController it's reduced to the one I'm setting, but the memory still keeps adding up.
Here you can see a comparison of both methods:
You can try this code to make navigation controller to contain only one viewController.
-(IBAction)goToSecond:(id)sender
{
SecondViewController *secondVC = [[SecondViewController alloc]init];
[self.navigationController setViewControllers:#[secondVC] animated:NO];
}
Related
This really should work. This is one of the simplest things to achieve in iOS and for some reason it's just not working.
I have two view controllers in my storyboard. One is InitViewController and the other is ViewController, with a storyboard ID of Init and ViewOne respectively. I have a button on InitViewController that is running code to switch the views. The code is running properly but nothing happens despite that fact. Here is the code:
-(IBAction)NextPage:(id)sender{
ViewController *wc = [[ViewController alloc]
initWithNibName:#"ViewOne"
bundle:nil];
[self.navigationController pushViewController:wc animated:YES];
}
I imported ViewController.h, I just don't know why this isn't working.
initWithNibName: is for view controllers that you create in .xib files, not storyboards. To create a view controller from a storyboard use -[UIStoryboard instantiateViewControllerWithIdentifier:]
ViewController *wc = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewOne"];
[self.navigationController pushViewController:wc animated:YES];
You need a NavigationController - try to log self.navigationcontroller and you will see is null
In your storyboard add a UINavigationController set it as entrypoint, set InitViewController as root view controller and then you can use:
ViewController *wc = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewOne"];
[self.navigationController pushViewController:wc animated:YES];
I am trying to access an Array variable in a view controller in an application that is using storyboards.
BACKGROUND:
I have been following along with the Ray Wenderlich tutorial on Storyboards.
Once I finished the tutorial, I went back tried a different route, though I’m having trouble accessing a view controller. Everything is pretty much the same except my set up is the initial Scene is a View Controller. I am to the part where the author is adding some data to NSMutableArray in his table.
THEIR CODE THAT I AM USING AS A GUIDE
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
UINavigationController *navigationController = [tabBarController viewControllers][0];
PlayersViewController *playersViewController = [navigationController viewControllers][0];
playersViewController.players = _players;
I was hoping it would be a simple as replicating what I had seen with view controllers, passing along the appropriate type, but no.
I have View Controller > View Controller > Navigation View Controller > UITableViewController.
MY CODE:
UIViewController *vc = (UIViewController*)self.window.rootViewController;
UIViewController vc1 = [vc viewController][0];
UINavigationController *nc = [vc1 viewController][0];
SearchViewTableViewController *svc = [nc viewControllers][0];
svc.myarray = _myarray;
I have tried multiple combinations and am getting nowhere.
There has got to be a simpler way for me to reference classes/view/scenes.
Any help?
Make sure you are importing the ViewController header file.
Make sure you have given you ViewController a Storyboard identifier.
Then something like this should work:
MyViewController *myVC = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"MyViewController"];
myVC.myMutableArray = [NSMutableArray new];
....
Im using a page based application very similar to the standard template from apple (page based app). I want all the pages to be loaded and kept in memory at start up, but it doesnt work. I keep all the ViewControllers that UIPageViewController displays in an NSArray which I initiate. The problem is that the ViewControllers are not initilized until the PageViewController calls them. How do I force initiation of ViewControllers right here in the init method of the ModelController.
- (id)init
{
self = [super init];
if (self) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *historyViewController = [storyboard instantiateViewControllerWithIdentifier:#"HistoryViewController"];
historyViewController.restorationIdentifier= HistoryRestorationID;
UIViewController *totalViewController = [storyboard instantiateViewControllerWithIdentifier:#"TotalViewSlidingController"];
totalViewController.restorationIdentifier= TotalRestorationID;
PlugTableViewController *plugViewController = [storyboard instantiateViewControllerWithIdentifier:#"PlugSlidingViewController"];
plugViewController.restorationIdentifier= PlugRestorationID;
//Force initiation of ViewController here!
pages = [[NSArray alloc] initWithObjects:historyViewController,totalViewController,plugViewController, nil];
}
return self;
}
The reason why I want to initiate them directly, is because they take time to initiate.
The view hierarchy is not loaded until someone references its view property.
Try call [historyViewController view] or read historyViewController.view. It should do the job.
Try type-casting the object where you want to use this view, hopefully this will do the job.
PlugTableViewController *plugView=(PlugTableViewController *)[pages objectAtIndex:3];
And one more things, try to change the code
UIViewController *historyViewController = [storyboard instantiateViewControllerWithIdentifier:#"HistoryViewController"];
to
HistoryViewController *historyViewController = [storyboard instantiateViewControllerWithIdentifier:#"HistoryViewController"];
hopefully you have made a header class HistoryViewController but you are initlizing view by UIViewController
Another possible solution is to pre-load data instead of viewcontroller as view controller do not take time to load.
MOST UNRECOMMENDED way is to add all view as subview and keep them hidden, show them when you need them.
I have a custom ViewController which is an instance variable of my root viewController.
I intend to modally present it whenever a button is touch. Therefore the viewController will be presented and dismissed potentially many many times.
I obviously only want to alloc init my instance variable once as the modal viewController is not deallocated each time it's dismissed, so should I have code like this inside my button action to ensure that it's only alloc and inited once?:
if(!myViewController)
{
ViewController *myViewController = [[ViewController alloc] init];
}
[self presentViewController:myViewController animated:YES completion:NULL];
I usually use lazy instatiation in those cases:
Declare a property for your ViewController:
#property(nonatomic, strong) UIViewController *myViewController;
After that you can override the get of myViewController
-(UIViewController*) myViewController {
if(!_myViewController) {
_myViewController = [[UIViewController alloc] init];
}
return _myViewController;
}
This way you guarantee that was only instantiated once and is always there when you needed.
ATTENTION
This works well if you always use self.myViewController. I consider a good practice that properties' generated iVars should only be accessed in their setters/getters.
You can use the following way to ensure that only one instance of the view controller active at a time.
if(myViewController) {
[myViewController release];
myViewController = nil;
}
myViewController = [[ViewController alloc] init];
[self presentViewController:myViewController animated:YES completion:NULL];
You need to make myViewController as class variable.
I was having memory management issues, finally found out the problem, I keep instantiating new view controllers. When the app launches it goes straight to the FirstViewController which is an element inside of UITabBarController in the storyboard.
I then show FilterViewController with this method:
- (IBAction)searchOptions:(id)sender {
FilterViewController *ctrl = [[FilterViewController alloc] init];
[UIView transitionFromView:self.view toView:ctrl.view duration:1
options:UIViewAnimationOptionTransitionCurlUp completion:nil];
self.filterViewController = ctrl;
[self.navigationController pushViewController:self.filterViewController
animated:NO];
}
Which works fine and brings up FilterViewController which has its own .xib, so it is not in the storyboard.
Now when trying to pop back to FirstViewController I use this method:
- (IBAction)backToMap:(id)sender {
// i used the below when trying to push another view controller
/*UIStoryboard *storyboard = [UIStoryboard storyboardWithName
:#"MainStoryboard" bundle:nil];
FirstViewController *fvc = [storyboard
instantiateViewControllerWithIdentifier:#"FirstViewController"];
fvc.modalTransitionStyle = UIModalTransitionStyleCoverVertical;*/
[self.navigationController popViewControllerAnimated:YES];
}
However it doesnt do anything. Nothing at all, I cant see what is wrong here?
popViewontrollerAnimated is only used when you have pushed your view controller onto a navigation stack, so it won't do anything here unless there is one in your project.
When you use transitionFromView... you are replacing your current view with the new view so you will need to call it again to get back to your old one.