At the moment, I'm trying to call an existing UINavigationController's method from a scene.
My storyboard looks like:
->UINavigationController--(rootViewController)-->ViewController-->Scene
I need help understanding how to call the existing UINavigationController's custom method named showGameCenterMenu() from the scene. This is what I have so far:
UINavigationController *navRef = self.view.window.rootViewController.navigationController;
[navController performSelector:#selector(showGameCenterMenu)];
I understand that my method call is probably incorrect, but any help would be appreciated understanding this and whether or not I'm breaking best practice by directly referencing this...
If the navigation controller is the root view controller then you are navigating too far (so you probably get nil), you should have:
UINavigationController *navRef = (UINavigationController *)self.view.window.rootViewController;
(with a cast from UIViewController)
Cast the pointer you're getting to your custom navigation controller class and then call its method.
With the help of the great guys in the comments I was able to find a suitable solution..
(NavigationViewController is my custom UINavigationController class);
NavigationViewController *navRef = (NavigationViewController *)self.view.window.rootViewController;
[navRef showGameCenterMenu];
This allowed me to send a method call to my NavigationViewController which displays the Game Center Menu one Layer above the existing View Controller by presenting it with .topViewController appended. Thanks again guys :)
Related
Xcode Image
As you can see in the image attached I have the root navigation controller called - Notifications and a UIViewController called NotificationsController.
So my Question is how can i pass a variable from NotificationsController back to Notifications using Protocol and Delegate, because in this case there is no segue but a default relationship between them.
Is my question correct or is there another way to do what i need.
Any help is really appreciated
To reference the navigation controller from the view controller (it is an optional so you need to handle that):
// self is a UIViewController
self.navigationController
To reference the view controller from the navigation controller:
// self is a UINavigationController
let index = // Index of the view controller. You may need to iterate over viewControllers to find this.
self.viewControllers[index]
You don't really need to set up a delegate. As keithbhunter pointed out in his answer, a view controller has a navigationController property that will point to the navigation controller that manages it.
I suggest you define a protocol for the messages you want to send to your navigation controller, and then have your custom subclass of UINavigationController conform to that protocol.
Within the view controllers that are on the navigation controller's stack you can fetch a pointer to your navigation controller and cast it to type UINavigationController<myNavControllerProtocol>.
(UINavigationController that conforms to myNavControllerProtocol. I'm working in Objective-C these days and don't remember the exact syntax for that.)
I perform some data loading tasks from an Ojective§C class and once everything is loaded, I simply wants to display a Viewcontroller subclass prepared in a storyboard.
So when everything is ok, the following method is called:
- (void)loadingNextView
{
CABBndGSite *mySite = [CABBndGSite alloc];
CABBndGSelectLanguageViewController *vc = [[mySite myRootViewController].storyboard instantiateViewControllerWithIdentifier:#"SelectLanguageViewController"];
[[mySite myRootViewController] presentViewController:vc animated:YES completion:nil];
}
So I verified that myRootViewController is not nil. It's a UINavigationController class.
vc is not nil so it found my view in the storyboard.
Anyway, the presentViewcontroller message seems to doing what expected.
Certainly a stupid mistake but my poor iOS programming knowledge lets me in the fog!
I use this code from ViewController subclasses with success and as here I get a valid ViewController pointer, I don't understand why it doesn't work.
I also tried to implement the AppDelegate method explained here How to launch a ViewController from a Non ViewController class? but I get a nil navigation pointer. Maybe something not well connected in my application
May I have some explanation?
Kind regards,
UINavigationController maintains a stack of view controllers. You can access this stack through the viewControllers property. To present your view controller, you can:
(a) have the navigation controller push the new view controller on to
the stack (pushViewController:animated:);
(b) have the top view controller in the view controller stack present
the new view controller modally (presentViewController:animated:completion:), or;
(c) add the new view controller to the view controller stack array
manually by assigning a new viewControllers array to the navigation
controller's viewControllers property (setViewControllers:).
I'm just learning IOS development, I would like to pass data between ViewController. I'm using Storyboard and I'm using the "prepareForSegue" method.
My main question is about the pattern I found in many forums and blogs about this "transmission" of information. When the origin controller needs to pass data to a destination controller, the origin controller access the destination controller using the code :
[segue destinationViewController]
This is fine, the origin controller doesn't need to know exactly the destination controller details (I'm using protocols).
But when the destination controller is a NavigationController (a ViewController embedded in a NavigationController), it seems that the recommended practice is :
[[segue destinationViewController] topViewController]
But if I do that, it means that the origin controller must know that the destination controller IS a NavigationController. I would like to avoid that if possible ?
Maybe I'm doing something wrong ? Is there another way to do it ?
The origin controller is a "detail page" (coming from a TableView), the destination controller is the "edit page".
Any help is welcome.
Thanks
UINavigationController *navTmp = segue.destinationViewController;
YourController * xx = ((YourController *)[navTmp topViewController]);
xx.param = value;
I see two possibilities:
Use -isKindOfClass and check if it's a UINavigationController
Create a protocol whith a -rootViewController method, create two categories conforming the protocol, one on UIViewController, another on UINavigationController, implement both, the one for UIViewController should return self, the one for UINavigationController should return topViewController. Now you'll be able to drop those categories on any controller, and use [[segue destinationViewController] rootViewController]
Check to see if the destinationViewController is a UINavigationController, and if it is, then get its topViewController. That way it just automatically handles either case, and it's safe.
But if I do that, it means that the origin controller must know that
the destination controller IS a NavigationController. I would like to
avoid that if possible ?
One possible solution is to subclass UINavigationController such that it can accept whatever data your source controller provides and in turn passes that data on to its root view controller. That might make particular sense if you have a number of segues, some leading to nav controllers and some not, and you want to handle them all the same way.
In other words, create a UINavigationController subclass that acts like a proxy for its root controller.
I'm working on an app that uses NavigationController based Storyboard, but the navigation is controlled by a segmentedControl. This way I can iterate over the x menus several times, then I can go back for about a year :D
My question is:
How can I override the push methods of the NavigationController to check if there's already an instance in the stack and reuse that without adding the same instance to the stack again?
Thank you in advance!
Subclass the UINavigationController and implement the pushViewController:animated: method in the subclass. There you can iterate through the stack by calling self.viewControllers which returns an NSArray of the view controllers on the stack. There you can check, whether the controller you're trying to push has already been pushed before. If not, call [super pushView...]. If it has, create a mutable copy of the stack NSMutableArray * newStack = [self.viewControllers mutableCopy] and move the desired view controller to the end of the array. Once you have that, just call [super setViewControllers:newStack animated:...] which sets your new stack manually.
I don't think you need to override the popViewController method, though.
Oh, and once you have that, don't forget to change the class of the UINavigationController in the Storyboard to the name of your subclass of the navigation controller.
Another thought - if you're using the segmented control, have you thought about using UITabBarController instead of UINavigationController for navigation?
I may go mad very soon.
This is the reason:
- I started up with Single View Application project with storyboards. Then I set the view controller class name in the storyboard for my viewController.
- Next step I created one pointer for this viewController in AppDelegate method ...didFinishLaunchingWithOpt... and filled it up by calling [myStoryboards instantiate...]. It works pretty good because I can call method like [vc1 setMyName] which does smthng like self.myName = #"Johnny";
- But here it comes. When I create IBAction method joined with button, this method doesn't know anything about "Johhny". And this is because I'm in another instance. When I check the address of "self" it is another one...
WhyWhyWhy??? Please help, how can I use still the same object - the one instantiated in AppDelegate by storyboards and the one from storyboards in "interface builder".
Thank you.
Oh my. I think I really underestamated it...
When we were talking about getting pointer of other viewControllers from storyboard...
I have initialViewController got by calling rootViewContr... And another one connected with segue (modal) where is UITableView. A get data on rootViewController and I want to show them on the other one in the list (UITableView). So I call segue (performSegueWithIdentifier), the other controller is shown but the list is clear. Because the method I call is working with tableView variable which is null :/ Because, again, I'm in another object. That is because I call that method storyboard instantiate... How can I get exactly the same viewController which I'm working in storyboard with. It is quite confusing for me :/
I read something about prepareForSegue and getting the pointer by destinationViewController but that is not what exactly I want. I need the pointer before I call segue method and the viewController is shown...
Thank you.
If you've set up your initial view controller properly in the storyboard, you don't need to assign it to the windows rootViewController property in -applicationDidFinishLaunchingWithOptions: as this is done for you automatically. It sounds like you're creating a second instance. To access the original instance setup by the storyboard simply do this in -applicationDidFinishLaunchingWithOptions:
InitialViewController *viewController = (InitialViewController *)self.window.rootViewController;
viewController.myName = #"Johnny";