Passing Array from RightChildViewController to MainVIewController - ios

I am having problem working with a SlidingViewController. My sliding view controller has a left view controller, a main view controller and a right view controller. My left view controller is a menu style view controller and switches the main view controller to a view controller selected. The right view controller is a table view controller that has recent searches using NSUserDefault keys. This key is an array that contains 4 strings. The problem I am having is sending this string back to the main view controller because the main view controller's viewWillAppear does not get called because technically it is never off the screen (ie it never disappears).
I've tried to make the view controller call the viewDidAppear manually but it seems as initializing the main view controller creates a whole new viewController all together and doesn't create the tableView inside the main view controller, therefore doesn't load reload a table view. Also, the tableView comes out as (null) when I log it.
Here is the didSelectRowAtIndexPath of LeftViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIStoryboard *iPhoneStoryBoard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
ViewController *viewController = (ViewController *)[iPhoneStoryBoard instantiateViewControllerWithIdentifier:#"HomePage"];
viewController.reloadArray = self.recentSearchesArray[indexPath.row];
NSLog(#"Recent ReloadArray: %#", viewController.reloadArray); //This prints out the correct information
/*
MenuViewController *menuViewController = [[MenuViewController alloc] initWithNibName:#"MenuViewController" bundle:nil];
SlidingViewController *slidingViewController = [[SlidingViewController alloc] initWithNibName:#"SlidingViewController" bundle:[NSBundle mainBundle] mainViewController:viewController leftViewController:menuViewController andRightViewController:self];*/ //Tried doing this too, doesn't work
[viewController viewWillAppear:YES];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
[self performSelector#selector(toggleView)];
}
viewWillAppear's of ViewController
-(void)viewWillAppear:(BOOL)animated
{
if(self.reloadArray.count !=0)
{
NSLog(#"Reload Array Not Empty!");
NSLog(#"ReloadArray: %#",self.reloadArray); // Logs out the correct information
[self.homePageTableView reloadData];
NSLog(#"Table: %#", self.homePageTableView); // Logs out as (null) :(
}
else
{
NSLog(#"Reload Array Is Empty");
}
}
Anyone have any suggestions/ideas or has anyone worked with something like this or come across this problem? If so, any information and guidance would be greatly appreciated. Thanks in advance!

You can use Notification center method. In your main view controller's viewdidload method write following code..
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(anymethod:)
name: anyname
object: nil];
- (void)anymethod:(NSNotification *)notification {
NSLog(#"%#", notification.userInfo);
}
and in your right view controller pass data like,
[[NSNotificationCenter defaultCenter] postNotificationName:#"anyname" object:self userInfo:anydata];

Related

Unable to pass data from one child viewcontoller to another childviewcontoller

I am using this to implement ContainerViewContoller. Everything is going fine but I'm unable to pass data from one child ViewController to an other child ViewContoller.
CouponCodeViewController *couponVC = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"CouponCodeViewController"];
couponVC.coponcode=#"this is data";
couponVC.title = #"Enter Coupon Code";
[couponVC viewWillAppear:true];
CategoryViewController *categoryVC = [self.storyboard instantiateViewControllerWithIdentifier:#"CategoryViewController"];
categoryVC.title = #"Choose A Category";
YSLContainerViewController *containerVC =
[[YSLContainerViewController alloc]
initWithControllers:#[couponVC,categoryVC]
topBarHeight:statusHeight + navigationHeight
parentViewController:self];
When I call my container ViewController it loads all childVC at the same time. Now I want to pass data on click from CouponCodeViewController to CategoryViewController but I'm not able to do so because viewDidLoad, viewWillappear, and viewDidAppear are not called
CouponCodeViewController I am going using this:
- (IBAction)skipAct:(id)sender {
// CategoryViewController *category=[[CategoryViewController alloc]init];
// category.userInput=#"this is the data";
[self.scrollMenuDelegate scrollMenuViewSelectedIndex:1];
}
#pragma mark -- YSLContainerViewControllerDelegate
- (void)containerViewItemIndex:(NSInteger)index currentController:(UIViewController *)controller
{
[controller viewWillAppear:YES];
}
In CategoryViewController my viewDidLoad and viewDidAppear are not called.
How can I pass data from childVC to another ChildVC.
That because when you init all controllers, the parent controller doesn't do 'addChildViewController'. It does that only when showing a new controller (I've looked the code).
And after it move from this controller, it removes it from the controller hierarchy.
I would use either delegate or notification to pass the data.
Another option (which I don't like) is to give the coupons controller a reference from the categoryViewController.

viewDidLoad() method not called after simultaneous pop and push to same view

I am using this code for the navigation view controller in the start
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
splashViewController *rootVC = [[splashViewController alloc]initWithNibName:#"splashViewController" bundle:nil];
self.navigator = [[UINavigationController alloc] initWithRootViewController:rootVC];
self.window.rootViewController = self.navigator;
[self.window makeKeyAndVisible];
after that i am using this simple method to push to next view
ScoreboardListViewController *SLvc = [[ScoreboardListViewController alloc]initWithNibName:#"ScoreboardListViewController" bundle:nil];
[self.navigationController pushViewController:SLvc animated:YES];
and using this to pop out from the view
[self.navigationController popViewControllerAnimated:YES];
but when i poppet and again push to the same view with different property values then viewdidload method did not run
it only runs if i just to any other view and then push to this view
i was not able to understand this abnormal behaviour. as when ever i push to any view then this viewdidload should be executed.....
for example i am doing this to get to the view
->
SLvc.trigger = #"sold";
SLvc.lblHeader.text = value;
[self.navigationController pushViewController:SLvc animated:YES];
-> then i popped out by back button by using this
[self.navigationController popViewControllerAnimated:YES];
-> then i push to the same view with different property
SLvc.trigger = #"earning";
SLvc.lblHeader.text = value;
[self.navigationController pushViewController:SLvc animated:YES]
and this time viewdidload didn't run.
This is because you are holding a strong pointer to SLvc controller. When you pop it, the view controller is retained. Initializing a new controller every time you push will solve your problem.
// below method called when pop out from the view
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
You can try these in your viewcontroller:
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
// your code here
}
or
-(void)awakeFromNib
{
[super awakeFromNib];
// your code here
}
viewDidLoad() -Called after init(coder:) when the view is loaded into memory.
Similar to viewWillAppear, this method is called just before the view disappears from the screen. And like viewWillAppear, this method can be called multiple times during the life of the view controller object. It’s called when the user navigates away from the screen – perhaps dismissing the screen, selecting another tab, tapping a button that shows a modal view, or navigating further down the navigation hierarchy
viewDidLoad is called exactly once, when the ViewController is first loaded into the memory. This is where you want to instantiate any instance variables and build any views that live for the entire lifecycle of this ViewController. However, the view is usually not yet visible at this point.
However, viewWillAppear gets called every time the view appears.

Refresh table from another UIViewController

I have two view controllers one of them (ViewController) has a table called tableView.
I would like to refresh this table from my other view controller (pageView).
I have tried this in the pageView:
ViewController*refresh;
[refresh.tableView reloadData];
But this is not working.
The connecting segue between the two view controllers is a push segue
What should I do? Should I do it through a storyboard segue?
Option 1
#Class2
#property (nonatomic) BOOL shouldRefresh; // in .h file
- (void)viewWillAppear:(BOOL)animated // in .m file
{
[super viewWillAppear:animated];
if (_shouldRefresh) [self.tableView reloadData];
}
#Class1
// Add this in any method where you want it to refresh and push to view
ClassTwoController *viewController = [[ClassTwoController alloc] init];
[viewController setShouldRefresh:YES];
[self.navigationController pushViewController:viewController animated:YES];
*UPDATE:
Option 2
#Class 2
// Add this line in viewDidLoad in same class you want the tableView to refresh
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refreshTableWithNotification:) name:#"RefreshTable" object:nil];
// Add this method just beneath viewDidLoad:
- (void)refreshTableWithNotification:(NSNotification *)notification
{
[self.tableView reloadData];
}
#Class1
// Call this when ever you want to refresh the tableView in Class2
[[NSNotificationCenter defaultCenter] postNotificationName:#"RefreshTable" object:nil userInfo:nil];
/* Add this line in viewDidLoad method in ViewController (by using this line you are adding notification observer to ViewController class) */
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reloadTableview:) name:#"ReloadTableview" object:nil];
/* Add this method in ViewController class(this method call when you post notification in pageView) */
- (void)reloadTableview:(NSNotification *)notif
{
[tableView reloadData];
}
/* add this line when/where you want to refresh your (this line post notification which added in ViewController) */
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadTableview" object:nil];
If second view controller is already loaded then you can use NSNotification in firstviewcontroller. This notification will invoke a method in second view controller. In this method write a code to reload a tableview.
If I understand correctly, you want to reload a table view that exists in the previous view controller of your navigation stack. If so, you can access it through the navigation controller's viewControllers property.
NSUInteger indexOfTableController = self.navigationController.viewControllers.count - 2;
ViewController *tableController = (ViewController*)[self.navigationController.viewControllers objectAtIndex:indexOfTableController];
[tableController.tableView reloadData];
Most often this happens because you're not initializing the reference to your second controller properly and it stays nil. Has to be something like this.
#implementation
{
ViewController *refresh;
}
- (void) openNewController
{
refresh = [[ViewController alloc] init];
[self.navigationController pushViewController:refresh animated:true];
}
- (void) refreshNewController
{
[refresh.tableView reloadData];
}
#end
Okay for sake of example lets call the previous ViewController VC1 and the current ViewController VC2.
So you do a push segue from VC1 to VC2. What you could do is: pass a reference of the TableView to VC2 during the segue. So you would create a public UITableView tblView variable in VC2 and then in VC1 implement the prepareForSegue method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"Segue_Identifier_From_StoryBoard"])
{
// get your destination view controller
VC2 *vc2 = [segue destinationViewController];
vc2.tblView = TableView;
}
}
Then in VC2, you could just call [tblView reloadData];
Just make sure you nil the tblView reference once you are done with VC2.
In ViewController1 :
> Create property for UITableview with nonatomic and retain
> Now Create table Object.
In ViewController2.m:
ViewController1 *objV1 = [ViewController1 alloc]initWithNibName....];
[objV1.yourTableView reloadData];
You can check the adress of viewcontroller.tableview and refresh.tableView and i think they would be not the same;
you can make singleton for viewcontroller and then invoke for example
viewcontroller *vc = [viewcontroller sharedInstance];
[[vc tableview] reloadData];
In your pageView create a method
-(void) reloadVCController{
ViewController*vc=[[ViewController alloc]init]];
[vc.tableview reloadData;
}
And use it where you want to call it
[self reloadVCController];
ViewController*refresh;
[refresh.tableView reloadData];
By seeing the above code you are not assigning your ViewController instance to refresh. When executing reloadData() your refresh will be nil so obviously it won't react to any action.
ViewController*refresh = originalInstance; //assign your original instance here
[refresh.tableView reloadData];
this would work and the main point to be noted here is your refresh viewcontroller should be the top most instance in stack because any UI operation should happen in main thread.
Use NSnotification center add observer in that view controller and write method for reload table.
[self.tableView reloadData];
And from current view post notify that observer.
In case you have tried all the answers, make sure that before reloading the tableView, the dataSource is actually updated with new/additional/ content.
Reloaddata needs to execute within the parentviewcontroller because it invokes the tableview delegate methods to reload the table such as CellForRowAtIndexpath, and many more.
This means you need to define a public method in the parentviewcontroller that will contain the reloaddata command and call it from the child view controller.
public Method:
Parentviewcontroller.h
#interface
-(void) reloadTable:(id) sender;
parentviewcontroller.m
#implementation
- (void) reloadTable:(id) sender {
[self.tableview reloaddata];
return;
}
childviewcontroller.h
#import parentviewcontroller.h
#class parentviewController
#interface childviewcontroller :UIViewController (or UITableViewController)
#property (nonatomic, strong) parentViewController *parent;
childviewController.m
#implementation
#synthesize parent;
/* Note the parent instance is passed from the parent to the child view controller,
it is not allocated and initialised in the child controller - many ways to do this
depending on coding style */
/* after updating the data */
[parent reloadTable];
I think the problem is that you refresh controller view is not loaded. So, reloadTable message simply goes to nil. You can force a View Controller to load its view by calling view getter:
[viewController view];
So check if your tableView is nil and if it is, initialise it with described method.
As a make-up answer to emotality's:
If you want to skip the start-up animation, just simply set a bool property in your ClassTwoController to check if it's first time loading this view
ClassTwoController.h:
#property (nonatomic, assign) BOOL notFirstTimeRun;
ClassTwoController.m:
-(void)viewWillAppear:(BOOL)animated // in .m file
{
if(self.notFirstTimeRun) {
[self.tableView reloadData];
} else {
[self playStartUpAnime];
}
}
-(void)playStartUpAnimation // in .m file
{
//Place your codes for animation here
}
in your ClassOneController.m initialise it with the check bool:
ClassTwoController *viewController = [[ClassTwoController alloc] init];
viewController.notFirstTimeRun = YES;
If you have parent-child relationship you can add weak property to child viewcontroller like this
#property (nonatomic, weak) ParentViewController *parentController;
Then when you need to reload table you just call [parentController.tableView reloadData]
Other way would be using NSNotificationCenter. In controller you want to reload table you should subscribe to notification([[NSNotificationCenter defaultCenter] addObserver...]) and implement selector you supply to this method. In another controller you should post notification with that name.
Also you can just refresh on viewWillAppear if it suits your needs.

How to set up a delegate to a class without the use of prepareForSegue

I have two classes I would like them to speak with each other. Class A contains a tableView and when users hitting a table row, I fire my didSelectRowAtIndexPath method. In this method I need to inform class B about this through a delegate. I know how delegates work but having a hard time to figure how to set the delegate of A without using the prepareForSegue method.
Normally I would do this when I set up my delegate
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"goToManipulator"]) {
ManipulatorViewController *secondVC = (ManipulatorViewController *) segue.destinationViewController;
[secondVC setDelegate:self];
}
}
But how can I set the delegate without the use of prepareForSegue?
Thanks in advance
EDIT :
This is how the structure of my storyboard looks like. The "receiver" viewcontroller is the one that will get the data and display in the "current name" label depending on what's been selected in the tableview from the "sender" viewcontroller, closest to the right.
http://oi62.tinypic.com/2li99w1.jpg
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ManipulatorViewController *secondVC = [[ManipulatorViewController alloc] init...];
[secondVC setDelegate:self];
//if you use push transition in UINavigationController
[self.navigationController pushViewController:secondVC animated:YES];
//if you use modal transition
[self presentViewController:secondVC animated:YES completion:nil]
}
init... means that initialization depends on your program architecture.
EDIT
If you want to get secondVC from storyboard, use
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ManipulatorViewController* secondVC = [storyboard instantiateViewControllerWithIdentifier:#"secondVC"];
And don't forget to add identifier for your viewController in storyboard.
I understand your use case like this:
In the Receiver, you open the Sender. There you select a value, and after selecting the value you want to tell the Receiver about the new value.
You can create a protocol on Sender, that Receiver implements. Then, in the function that catches the chosen value in Sender, you call the protocol method (e.g. didSelectNewName() or something).
Of course, you need a handle to the Receiver, which you typically get via the delegate. But wether you use a segue or other method to transition from Receiver to Sender, you will all the same have the opportunity to set the delegate of the Sender.
If this is not what you are looking for, please explain exactly how you initialize the Sender, and why segue is not desirable.
Is View Controller B already instantiated when A's cells are tapped? If it is and you're not using prepareForSegue to get the other View Controller's identity, it might be better to use NSNotification Center. In View Controller A's didSelectRowAtIndex method, you can put
[[NSNotificationCenter defaultCenter] postNotificationName:#"yourNotificationName" object:nil userInfo:dictionaryWithYourData];
and it will put up a notification to your whole app that the row was selected. If you initialize a dictionary with any info you want before hand, it can be passed through userInfo. Then, in View Controller B's viewDidLoad, add
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourMethod:) name:#"yourNotificationName" object:nil];
to make it listen for the notification. The selector you set will accept the NSNotification as a parameter, so you can get the dictionary as follows:
- (void)yourMethod:(NSNotification *)notification
{
NSDictionary *yourData = [notification userInfo];
}
Here's what I do.
In the .m file:
#implementation ViewController{
SecondViewController *svc;
}
And then below you need to an action like this:
- (IBAction)goToView2:(id)sender {
if (!svc) {
svc = [[self storyboard] instantiateViewControllerWithIdentifier:#"View2"];
[svc setDelegate:self];
}
[[self navigationController] pushViewController:svc animated:YES];
}
Just make sure to set the correct identifier in the StoryBoard to the ViewController where protocol is declared.

How to pop back to root view controller but then push to a different view?

I am writing a simple application that has 3 view controllers. The root view controller is an item listing, basic table view. Off of this view controller, I push two different view controllers based on some user interaction - a create item view controller or a view item view controller.
So, the storyboard segues just look like a V, or something.
On my create item view controller, I would like it to pop back to the root view controller when the user creates a new item, but then push to the view item controller so that I can look at the newly created item.
I can't seem to get this to work. It's easy enough to pop back to the root view controller, but I'm unable to push that view item controller.
Any ideas? I've pasted my code, below. The pop function works, but the new view never appears.
- (void) onSave:(id)sender {
CLLocation *currentLocation = [[LocationHelper sharedInstance] currentLocation];
// format the thread object dictionary
NSArray* location = #[ #(currentLocation.coordinate.latitude), #(currentLocation.coordinate.longitude) ];
NSDictionary* thread = #{ #"title": _titleField.text, #"text": _textField.text, #"author": #"mustached-bear", #"location": location };
// send the new thread to the api server
[[DerpHipsterAPIClient sharedClient] postPath:#"/api/thread"
parameters:thread
success:^(AFHTTPRequestOperation *operation, id responseObject) {
// init thread object
Thread *thread = [[Thread alloc] initWithDictionary:responseObject];
// init view thread controller
ThreadViewController *viewThreadController = [[ThreadViewController alloc] init];
viewThreadController.thread = thread;
[self.navigationController popToRootViewControllerAnimated:NO];
[self.navigationController pushViewController:viewThreadController animated:YES];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self.navigationController popToRootViewControllerAnimated:YES];
}];
}
If I understand you correctly, you have a stack of view controllers:
A (root) - B - C - D - E
And you want it to become:
A (root) - F
Right? In that case:
NSArray *viewControllers = self.navigationController.viewControllers;
NSMutableArray *newViewControllers = [NSMutableArray array];
// preserve the root view controller
[newViewControllers addObject:[viewControllers objectAtIndex:0]];
// add the new view controller
[newViewControllers addObject:viewThreadController];
// animatedly change the navigation stack
[self.navigationController setViewControllers:newViewControllers animated:YES];
Swift 4
// get current view controllers in stack and replace them
let viewControllers = self.navigationController!.viewControllers
let newViewControllers = NSMutableArray()
// preserve the root view controller
newViewControllers.add(viewControllers[0])
// add the new view controller
newViewControllers.add(viewThreadController)
// animatedly change the navigation stack
self.navigationController?.setViewControllers(newViewControllers as! [UIViewController], animated: true)
I think
[self.navigationController pushViewController:viewThreadController animated:YES];
is using a different NavigationController than the statement before that.
Because after popping to the root view Controller you loose the navigation Controller you are in. Solve that using this code instead
UINavigationController *nav = self.navigationController;
[self.navigationController popToRootViewControllerAnimated:NO];
[nav pushViewController:viewThreadController animated:YES];
I also think that this wont solve your whole problem. You will probably get an error saying that two fast popping and pushing may invalidate the NavigationController.
And to solve that you can either push the NavigationController in the viewDidDissappear Method of the 2nd View Controller or push it in the viewDidAppear Method in the Main View Controller(item listing).
An easy way to accomplish what you want to do is to build some simple logic into your main root view controllers -(void)viewWillAppear method and use a delegate callback to flip the logic switch. basically a "back reference" to the root controller. here is a quick example.
main root controller (consider this controller a) - well call it controllerA
set a property to keep track of the jump status
#property (nonatomic) BOOL jumpNeeded;
setup some logic in
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.jumpNeeded ? NSLog(#"jump needed") : NSLog(#"no jump needed");
if (self.jumpNeeded) {
NSLog(#"jumping");
self.jumpNeeded = NO;
[self performSegueWithIdentifier:#"controllerC" sender:self];
}
}
Now, in your main root controller,when a tableview row is selected do something like this
when pushing to controllerB in your tableView did select method
[self performSegueWithIdentifer#"controllerB" sender:self];
then implement your prepare for segue method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
//setup controller B
if([segue.identifier isEqualTo:#"controllerB"]){
ControllerB *b = segue.destinationViewController;
b.delegate = self; //note this is the back reference
}
//implement controller c here if needed
}
Now move on to controllerB
you need to set a property called "delegate" to hold the back reference
and you need to import the header file from the root controller
#import "controllerA"
#property (nonatomic,weak) controllerA *delegate;
then just before you pop back to controllerA, you set the flag
self.delegate.jumpNeeded = YES;
[self.navigationController popViewControllerAnimated:YES];
and that is about it. You don't have to do anything with controllerC. There are a few other ways to do, but this is pretty straight forward for your needs. hope it works out for you.
Sharing a category on UINavigationController based on Dave DeLong's answer that we use in our application to keep the back button always working as required.
#implementation UINavigationController (PushNewAndClearNavigationStackToParent)
- (void) pushNewControllerAndClearStackToParent:(UIViewController*)newCont animated:(BOOL)animated
{
    NSArray *viewControllers = self.viewControllers;
    NSMutableArray *newViewControllers = [NSMutableArray array];
    
    // preserve the root view controller
    [newViewControllers addObject:[viewControllers firstObject]];
    // add the new view controller
    [newViewControllers addObject:newCont];
    // animatedly change the navigation stack
    [self setViewControllers:newViewControllers animated:animated];
}
#end
NSArray *viewControllers = self.navigationController.viewControllers;
NSMutableArray *newViewControllers = [NSMutableArray array];
// preserve the root view controller
for(int i = 0; i < [viewControllers count];i++){
if(i != [viewControllers count]-1)
[newViewControllers addObject:[viewControllers objectAtIndex:i]];
}
// add the new view controller
[newViewControllers addObject:conversationViewController];
// animatedly change the navigation stack
[self.navigationController setViewControllers:newViewControllers animated:YES];
I don't think this is possible because popping back will deallocate everything.
I think a better way is to put your data on a model singleton class, pop to the rootviewcontroller and listen for the pop to end. Then you check if there is some data stored on the model so that you know if you should push a new viewcontroller.
I used Dave's answer as inspiration and made this for Swift:
let newViewControllers = NSMutableArray()
newViewControllers.addObject(HomeViewController())
newViewControllers.addObject(GroupViewController())
let swiftArray = NSArray(array:newViewControllers) as! Array<UIViewController>
self.navigationController?.setViewControllers(swiftArray, animated: true)
Replace HomeViewController() with whatever you want your bottom view controller to be
Replace GroupViewController() with whatever you want your top view controller to be
If what you want to do is navigate in any way inside the same navigation controller... you can access the navigationStack A.K.A. viewControllers and then set the viewcontrollers in the order that you want, or add or remove some...
This is the cleanest and native way to do it.
UINavigationController* theNav = viewControllerToDismiss.navigationController;
UIViewController* theNewController = [UIViewController new];
UIViewController* rootView = theNav.viewControllers[0];
[theNav setViewControllers:#[
rootView,
theNewController
] animated:YES];
do any necessary validations so you don't get any exception.

Resources