I have the following setup:
SplitViewController as rootController.
Detail part is first
ViewController with View > ContainerView (later, View will have ImageView, but that is not the issue here).
The ContainerView has segue (embed) to another view controller (NavigationController).
This is graphically represented in IB as:
Now the thing is I want to access the NavigationController from rootController (eg SplitViewController). I was unable to navigate down the hierarchy of "subViews" and so.
Is there some convenient way to get hold of the NavigationController?
Without the ViewController (together with ContainerView), I was able to access it like:
UISplitViewController *splitViewController = (UISplitViewController *) self.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
// now i have the controller, i can delegate to it or use it in any other way:
splitViewController.delegate = (id) navigationController.topViewController;
When you add the NavigationController, you could pass it upwards (delegate methods) to your subclass of UISplitViewController and store a reference there.
You could add a #property (MySplitViewController *) delegate; your MyContentView and set it to the splitviewcontroller. When the segue is fired, you could then do the following:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowNavigationController"])
{
UINavigationController *controller = segue.destinationViewController;
[self.delegate setNavigationController:controller];
}
}
Edit: If you want to stick to your code, you could do something like this:
UISplitViewController *splitViewController = (UISplitViewController *) self.window.rootViewController;
UIViewController *container = [splitViewController.viewControllers lastObject];
UINavigationController *navigationController = [container.childViewControllers lastObject];
splitViewController.delegate = (id) navigationController.topViewController;
In that case you should really include some error handling.
Related
Apple documentation says that any application using a split view controller should make it as a root view controller. But i am struck at a state where, my login screen should redirect me to a split view controller. Is there a way to achieve this?
I am Using storyboards and new to programming. Kindly help.
One quite common way to solve this issue to change the rootViewController of your applications main UIWindow (which again is a property of your AppDelegate) after a successful login.
So, the initial view controller of your app needs to be your LoginViewController that handles the login. After a successful login, you can do something like this:
- (void)switchToMainInterface
{
// Change the root view controller of the application window to the main storyboard
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
UISplitViewController *mainSplitViewController = [mainStoryboard instantiateViewControllerWithIdentifier:#"MainSplitViewController"];
UIWindow *mainApplicationWindow = [[[UIApplication sharedApplication] delegate] window];
mainApplicationWindow.rootViewController = mainSplitViewController;
}
Note that this code is just dummy code to make my suggestion a bit more tangible, it makes the following assumptions:
you have a Storyboard called Main in your application bundle
within this Main Storyboard you have a UISplitViewController with the Storyboard ID MainSplitViewController so that you can instantiate it programmatically
you need to import AppDelegate.h to get access to the root UIWindow
Link your LoginViewController to a UIViewcontroller. In this controller drag an UIContainerView, and embed your UISplitViewController in it.
I created a custom segue class and implemented the following code. I am not sure what it does to my application. It seems a bit high level code to me as I am an amature but it works fine. Hope you find it useful.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Override point for customization after application launch.
// UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
// UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
// splitViewController.delegate = (id)navigationController.topViewController;
return YES;
}
Commented the above code, I believe this is to pause UISplitViewController from loading into UIWindow.
And my custom segue --> segue.m is as follows..
#import "Seague.h"
#implementation Seague
-(void)perform
{
UIViewController *sourceViewController = (UIViewController *)self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *)self.destinationViewController;
UISplitViewController *splitViewController = (UISplitViewController *)destinationViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
UIWindow *window = [UIApplication sharedApplication].keyWindow;
window.rootViewController = destinationViewController;
window.rootViewController = sourceViewController;
[UIView transitionWithView:sourceViewController.view.window duration:0.5 options:UIViewAnimationOptionTransitionNone animations:^{
window.rootViewController = destinationViewController;
} completion:^(BOOL finished){}];
}
#end
This segue is triggered when my login button is pressed and the login details are valid.
My rootViewController is the viewController that has my login button and not UISplitViewController.
Reference: This is not a code I wrote. Found it somewhere on web after searching for 2 days. Will update the source link for reference soon.
Thankyou all for your responses :)
I want to pass the variable " uid " between the FirstViewController and SecondViewController, so I used the following code to pass it. It successfully pass it, but the problem it hides the NavigationBar and the TabBar !
How can I solve it?
SecondViewController *vc2 = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
vc2.uid = uid;
[self.navigationController pushViewController:vc2 animated:YES];
Why dont you use storyboard segues to solve this? Just embed you view controller on a Navigation View controller and call performSegueWithIdentifier function.
Just add prepareForSegue function to your class and set uid there:
- (void) prepareForSegue: (UIStoryboardSegue *) segue sender: (id) sender
{
if([segue.identifier isEqualToString:#"secondViewSegue"])
{
//**OPTION A**: If you are using Push segue
SecondViewController *secondVC = (SecondViewController
*)segue.destinationViewController;
//**OPTION B**: ***ELSE, If you are using a Modal segue
UINavigationController *navController = (UINavigationController*)segue.destinationViewController;
SecondViewController *secondVC = (SessionViewController *)navController.topViewController;
//END OF OPTION B
secondVc.uid = uidValue;
}
}
hy,
i have a normal spitviewapplication with 1 master and 1 detail, to communicate between both i set this code in appDelegate:
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
ImageGaleryViewController *rightViewController = (ImageGaleryViewController *)navigationController.topViewController;
splitViewController.delegate = (id)navigationController.topViewController;
UINavigationController *masterNavigationController = splitViewController.viewControllers[0];
CatalogPopUpViewController *controller = (CatalogPopUpViewController *)masterNavigationController.topViewController;
controller.delegate = rightViewController;
now i want to make a second master and be able to switch between them in the detail, like having two buttons and click one opens 1s master other button opens 2nd master, but want to be abble to comunicate from the master to the detail too, setting a delegate, i can make a new viewcontroller and make a segue to replace with master, but how can i set the delegate? to comunicate from the masters to the detail? i hope i made my problem understadable.
i got it , i used
[self performSegueWithIdentifier:#"catalogMasterSegue" sender:self];
to call the master with, the segue then in prepare for segue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"catalogMasterSegue"])
{
CatalogPopUpViewController *controller=[[[segue destinationViewController]viewControllers]objectAtIndex:0];
controller.delegate=self;
}
}
I have a SplitViewController App with 2 initial controllers:
leftViewController (master)
rightViewController (detail)
my rightViewController (detail) has the delegate of the splitViewController for presenting a button which shows/hides my menu (master)
my leftViewController (master) is a menu application, when I select any element of the menĂº I trigger a segue connected to my master and it replaces my detailViewController for the selected element of menu
when I do that and if I try to rotate my iPhone for hide menu my app crashes and says:
*** -[rightViewController respondsToSelector:]: message sent to deallocated instance
I guess it is because my splitViewController wants to communicate with its delegate, its old rightViewController, but it is gone, it has been replaced on my view,
maybe I need whether:
reasign my delegate to my new viewController (detail)
or
remove delegate of my rightViewController and assign it then to my newViewController
also tried this in my new viewController:
#interface newViewController ()<UISplitViewControllerDelegate>
#end
#implementation newViewController
- (void)viewDidLoad
{
UISplitViewController *splitViewController = (UISplitViewController *)self.view.window.rootViewController;
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
splitViewController.delegate = (id)navigationController.topViewController;
}
...
#end
but still not working... I get the same message
how do I fix this???
thanks in advance
EDIT: add my segue code for helping to answer my question
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"viewSceneAgenda"]) {
[segue.destinationViewController setTitle:#"Citas"];
[segue.destinationViewController setUserIDElement:UID_CUS];
[segue.destinationViewController setOverallAppointments:overallDates];
}
}
Maybe you have to pass your delegate as you said to your newViewController... why don't you add this in your leftViewController (master)...
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"showNewViewControllerScene"]) {
UISplitViewController *splitViewController = (UISplitViewController *)self.view.window.rootViewController;
splitViewController.delegate = segue.destinationViewController;
}
}
just replace the segue identifier (showNewViewControllerScene) to match with yours on your storyboard
Gonna complement Jesus Answer
Since I'm working with several segues, all of them must have the delegate of my UISplitView, also since iPhone doesn't support splitviews, I added a condition for assigning the delegate just if the user is running this app on any iPad
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([[UIDevice currentDevice].model hasPrefix:#"iPad"]) {
UISplitViewController *splitViewController = (UISplitViewController *)self.view.window.rootViewController;
splitViewController.delegate = segue.destinationViewController;
}
if ([segue.identifier isEqualToString:#"viewSceneAgenda"]) {
[segue.destinationViewController setTitle:#"Citas"];
[segue.destinationViewController setUserIDElement:UID_CUS];
[segue.destinationViewController setOverallAppointments:overallDates];
}
}
I have a TabBarController with three tabs. The views for all tabs were embedded in their own navigation controller, except one, the Map view.
To navigate from one of the other views to the Map view and pass data I used:
- (IBAction)mapButton:(id)sender {
MapViewController *destView = (MapViewController *)[self.tabBarController.viewControllers objectAtIndex:0];
if ([destView isKindOfClass:[MapViewController class]])
{
MapViewController *destinationViewController = (MapViewController *)destView;
destinationViewController.selectedTruck = _truck;
}
[self.tabBarController setSelectedIndex:0];
}
And it was working. Now I need to embed the Map view in a navigation controller as well, to add a detail view, but when I do no data is passed, it only goes to the Map view.
Can anyone see what am I missing?
[self.tabBarController.viewControllers objectAtIndex:0] is no longer an instance of MapViewController. It is a UINavigationController with a MapViewController as its root view controller.
You could access the MapViewController via the UINavigationController like so but all these type casting assumptions are messy -
- (IBAction)mapButton:(id)sender {
UINavigationController *navigationController = (UINavigationController *)[self.tabBarController.viewControllers firstObject];
if ([navigationController isKindOfClass:[UINavigationController class]])
{
MapViewController *rootViewController = (MapViewController *)[navigationController.viewControllers firstObject];
if ([rootViewController isKindOfClass:[MapViewController class]])
{
MapViewController *destinationViewController = (MapViewController *)rootViewController;
destinationViewController.selectedTruck = _truck;
}
}
[self.tabBarController setSelectedIndex:0];
}
Instead, a better design would be to keep a reference to the MapViewController (as a property) when you set up the tab bar. That way you would simply call self.destinationViewController.selectedTruck = _truck; instead.