I would like to present a full screen ViewController without any knowledge about the current ViewController hierarchy. My current solution to find the ViewController on which I can always present my full screen ViewController is the following:
+ (UIViewController*) topMostController
{
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
return topController;
}
The background why I need this: our app uses a lot of
[[UIApplication sharedApplication]openURL:[NSURL urlWithString:#"http://www.someurl.com"]];
calls which open Safari externally. Apple started to reject this, because they think it's detrimental to the user experience, and we should use SFSafariViewController instead. But unlike openURL this requires a reference to a ViewController on which we can present SFSafariViewController. I don't want to change the code to acquire the appropriate ViewController at dozens of places, instead a universal method would be nice which gets the appropriate ViewController. The code I listed works every single time in our app, but I am unsure if it's really universal.
Related
In React Native project I want to access the Presented Viewcontroller from iOS npm module. I'm able to access the rootviewcontroller of the RN project using the below code
UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController;
But I want the current VC that is presented on top of RootVC so that I should be able to present native(iOS) UINavigationController on top of it.
Note:[UIApplication sharedApplication].delegate.window.rootViewController.presentedViewController returns nill.
I know this was asked a long time ago, but I was running into the same problem today ([UIApplication sharedApplication].delegate.window.rootViewController.presentedViewController returns nil) and couldn't find the solution for a while, so I'll leave this here for anyone still looking.
There's a function in the React Native source code that allows you to determine the presented view controller. It seems like they use this for their ActionSheetIOS module (see this line). To use this in your own native module, add the following:
// Put this near the top of the file
#import <React/RCTUtils.h>
...
// Put this where you need access to the presented view controller
UIViewController *presentedViewController = RCTPresentedViewController();
Ready to make a fool out of myself:
I have this skeleton app, that has the PushWoosh notification classes in place. It works fine. I'm able to send a push message to my app.
For this to work, in my AppDelegate, there is a method called
- (void) onPushAccepted:(PushNotificationManager *)pushManager withNotification:(NSDictionary *)pushNotification
that allows me to fire off stuff when a notification is accepted.
Meanwhile, in my ViewController, I have a method like this:
-(void)loadURL{
NSLog(#"testing");
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:TOPIC]]];
[webView reload];
}
This one works fine when called from the ViewController itself.
However, when I try to call this method from within the 'onPushAccepted' method in the appDelegate, the webView does not show the desired URL, although, as indicated by the logging, the method IS called.
I guess this shows that I'm lacking some fundamental understanding of the working of this all.
Therefore, I would be satisfied with some some strings that would make this work, but I would be really happy with an explanation on the why and how behind it.
I tried putting the onPushAccepted: in the ViewController, but that didn't work at all, although I included the necessary "PushNotificationManager.h" in the ViewController.m.
I'm confused, and need your help.
I think your answer will get me close to getting the basics.
Thanks ahead!
In this specific case Sjakelien was using a Single View Application. The confusion was that the attempt to instantiate the ViewController in AppDelegate by doing the following ViewController * vc = [[ViewController alloc]init]; [vc loadURL]; did not work.
In this case the solution is the get the ViewController that is displayed on screen by using
- (void) onPushAccepted:(PushNotificationManager *)pushManager withNotification:(NSDictionary *)pushNotification {
ViewController *vc = (ViewController*)self.window.rootViewController;
[vc loadURL];
}
Applications with a different setup such as a UINavigationController need to take different actions.
A few choices:
popToRootViewController and instantiate a new instance of ViewController and push it onto the navigation stack
Push a new instance of ViewController onto the navigation stack without using popToRootViewController
Present the ViewController in a modal
Modify the model of the application
I am really new to IOS so I apologize if this questions is not worded clearly. I have tried searching around but I have not found exactly what I am looking for.
basically in my AppDelegate applicationDidBecomeActive method, I am making a call to my webservice to make sure that the user is still a valid user, and to pull down some refrehsed data, or kick them back to the login page if they are no longer valid.
The part that I am having trouble with is the second part. How can I load and show and specific ViewController(in this case the loginViewController) when the user is found to be invalid? I want to let the normal viewController flow happen when they are valid, which is is doing fine, but I can not figure out how to launch a specific viewController when I need to from AppDelegate.
Any ideas?
I think I got it! I used this code in the AppDelegate to display the ViewController I needed.
UIViewController *loginController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"LoginViewController"];
UINavigationController *loginNavController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"LoginNavController"];
UIViewController *currentVC = self.window.rootViewController;
_window.rootViewController = loginNavController;
[currentVC presentViewController:loginNavController animated:NO completion:nil];
For simplicity, lets say you have a one view app (not nav controller, not tab bar controller - the solution scales but easier to explain). When you get the appDelegate message that the app launched, then make a UIImageView the root view and show your launch image (user thinks you are still booting up). Try to log in, and do this in some other object (not a view controller). If you succeed, you make your desired view the rootView, and users sees it. If the login fails, then you makea login window the rootView. The key here is to have an object that is driving this and can interact with the appDelegate. You could also add this functionality to the appDelegate itself.
I want to present a ModalView, everytime my application receives a Pushnotification.
On arrival of the Notification, my app displays any of the UIViewControllers within the app.
I need this UIViewController to call
[presentingViewController presentModalViewController:vc animated:YES];
// presentingViewController is what I need :)
Is there a way to find out which UIViewController is currently active?
I would know how to do this with e.g. UITabbarController or UINavigationController, but I can't rely on the presence of either of these.
I also tried getting the rootViewController via
[[[UIApplication sharedApplication] keyWindow] rootViewController]
and presenting the ModalView from there, but - of course - it wouldn't show for any other pushed/displayed views.
I think the easiest way would be to get a global variable of type id or UIViewController that you update every time you display a controller . This way you will always have a reference to the current controller.
Hope this helps.
Cheers!
No matter which way i turn the iPad, the viewcontroller.interfaceOrientation always returns 1. Anyone know why this is?
It completely depends where your viewcontroller sits in the view heirarchy.
UIWindow should only have a single viewcontroller linked to it (usually a rootViewController) and this is the viewcontroller that responds to the rotation.
If the rootViewController has multiple children viewcontrollers, you'll need to detect the rotation in the rootViewController and pass it on to the relevant viewcontroller
There's a very good programming guide in the online docs about it. If I find the link I'll post it.
Search my previous posts where I was pointed in the same direction.
This problem seems to disappear in iOS 6.0.
A workaround could look like this:
- (UIInterfaceOrientation)interfaceOrientation;
{
UIViewController *rootViewController = self.view.window.rootViewController;
if (rootViewController) {
return rootViewController.interfaceOrientation;
}
return [super interfaceOrientation];
}