didReceiveRemoteNotification presentViewController from anywhere when running - ios

I'm trying to present a UIViewController when a remote notification is received.
My code works to point, but when the app is running and the user is on any other than the first screen/navigation stack, the UIViewController isn't presented.
Can anyone help please? Note I want to keep the navigation bar when the UIViewController is presented
I get this warning when I try to present the 'UIViewController' elsewhere
Warning: Attempt to present on whose view is not in the window hierarchy!
Thanks in advance
Here is my code for didReceiveRemoteNotification:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
var payload = userInfo
let requestID = payload["requestID"] as! String
let rootViewController = self.window!.rootViewController as! UINavigationController
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc : RequestViewController = storyboard.instantiateViewControllerWithIdentifier("RequestViewController") as! RequestViewController
vc.requestID = requestID
let navigationController = UINavigationController(rootViewController: vc)
rootViewController.presentViewController(navigationController, animated: true, completion: nil)
}

Fixed it by changing:
rootViewController.presentViewController(navigationController, animated: true, completion: nil)
to:
rootViewController.visibleViewController!.presentViewController(navigationController, animated: false, completion: nil)

Related

Issue on dismissing a viewController to rootViewController

I am trying to dismiss a viewController to rootViewController while signOut. But the problem is that the viewController is not getting dismissed, It still remains in the same page itself.
Below I have mentioned the code that I have used.
let AppDel = UIApplication.shared.delegate as! AppDelegate
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let login = mainStoryboard.instantiateViewController(withIdentifier: "login")
let nav = UINavigationController(rootViewController: login)
AppDel.window!.rootViewController = nav
AppDel.window?.rootViewController?.dismiss(animated: true, completion: nil)
(AppDel.window?.rootViewController as? UINavigationController)?.popToRootViewController(animated: true)
login.navigationController?.setNavigationBarHidden(true, animated: false)
Thanks in advance.
Earlier, I have faced the same issue. I was fixed issue by performing all other operation after dismissing controller successfully.
Please refer below sample code. I am sure it will work for you.
AppDel.window?.rootViewController?.dismiss(animated: true, completion: {
(AppDel.window?.rootViewController as? UINavigationController)?.popToRootViewController(animated: true)
login.navigationController?.setNavigationBarHidden(true, animated: false)
})
in the App delegate type a function that takes the new view controller and set it as root. than dismisses the old one.
func updateRootViewController(with viewController: UIViewController) {
guard let oldViewController = self.window?.rootViewController else { return }
UIView.transition(from: oldViewController.view, to: viewController.view, duration: 0.3, options: [.transitionCrossDissolve, .allowAnimatedContent]) { _ in
self.window!.rootViewController = viewController
self.window!.makeKeyAndVisible()
oldViewController.dismiss(animated: false) {
oldViewController.view.removeFromSuperview()
}
}
}
Why have you dismissed your navigation controller before calling popToRootViewController ?
AppDel.window?.rootViewController?.dismiss(animated: true, completion:
nil)
Check if you are calling this from the main thread.
Add your code inside this block:
DispatchQueue.main.async {
// TODO: Your code
}

How to go from appDelegate to viewController [duplicate]

This question already has answers here:
Storyboard - refer to ViewController in AppDelegate
(5 answers)
Closed 4 years ago.
if i have two viewControllers and i want when condition succeeded i want to go to another ViewController.
but i want to check every time the app launched so i stored the value on userDefaults and in appDelegate i checked for this value.
when i print the value i get it but i can not go to the desired controller.
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let active:Bool = defaults.bool(forKey: "activeBooking")
if active {
print("active \(active)")
let rootViewController = self.window!.rootViewController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Final", bundle: nil)
let setViewController = mainStoryboard.instantiateViewController(withIdentifier: "finalBooking") as! BookingFromCurrentLocationVC
rootViewController?.navigationController?.popToViewController(setViewController, animated: false)
}
return true
}
i print the value in other controller and it return me true(my value) so why i can not go to another controller
You can try this:
In appDelegate and in didFinishLaunching
You can store this value in UserDefault and then check the condition:
if condition == true{
goToVC1()
}else{
goToVC2
}
func goToVC1() {
let storyboard:UIStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let ObjVC1: ViewController = storyboard.instantiateViewController(withIdentifier: "VC1") as! VC1
let navigationController : UINavigationController = UINavigationController(rootViewController: ObjVC1)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
func goToVC2() {
let storyboard:UIStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let ObjVC2: ViewController = storyboard.instantiateViewController(withIdentifier: "VC2") as! VC2
let navigationController : UINavigationController = UINavigationController(rootViewController: ObjVC2)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
Add a segue from Navigation controller to desired View Controller (with segue identifier "finalBookingVC") and replace the code inside your if condition with:
self.window?.rootViewController!.performSegue(withIdentifier: "finalBookingVC", sender: nil)
You can initiate your view controller in AppDelegate as below.
Perform your condition check and set the StoryboardID and ViewControllerID as per your requirement.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
//check your condition here and change the Storyboard name and ViewController ID as required
let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "HomeVCID") as! HomeController
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
return true
}
The first thing you need to check if an application is active and already open (top controller is finalBookingVC) then no need to do anything,
second thing finalBookingVC if already available in a stack then no need to push at that time you need to pop view controller.
If finalBookingVC is not available on the stack then you need to push this controller.
func gotoController() {
let navigationController : UINavigationController! = self.window!.rootViewController as! UINavigationController;
let arrViewController = navigationController;
if arrViewController != nil && !(arrViewController?.topViewController is finalBookingVC) {
var finalBookingVCFound:Bool = false;
for aViewController in (arrViewController?.viewControllers)! {
if aViewController is finalBookingVC {
finalBookingVCFound = true;
_ = navigationController?.popToViewController(aViewController, animated: true);
break;
}
}
if !finalBookingVCFound {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil);
let objVC:finalBookingVC = mainStoryboard.instantiateViewController(withIdentifier: "finalBookingVC") as! finalBookingVC;
navigationController?.pushViewController(objVC, animated: true);
}
}
}

Trying to Embed VC into NavigationVC from performActionFor shortcutItem via 3d touch on App icon

I have an app that is using 3d touch to jump to a certain VC in my app. The issue is that when the app is launched normally, all of my VC's are embedded into a Navigation View Controller. But since I am skipping the launch sequence and jumping right into a VC, the VC that I jump to isn't embedded into the Nav VC.
Here is what I am trying in the App Delegate
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: #escaping (Bool) -> Void) {
guard TouchActions(rawValue: shortcutItem.type) != nil else {
print("3d not working")
completionHandler(false)
return
}
print("3d touch workig")
let mainStoryboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let addPersonVC : AddPersonViewController = mainStoryboard.instantiateViewController(withIdentifier: "AddPerson") as! AddPersonViewController
// pass the stack to the addPersonVC
addPersonVC.coreDataStack = coreDataStack
let navController:UINavigationController = mainStoryboard.instantiateViewController(withIdentifier: "navController") as! UINavigationController
navController.pushViewController(addPersonVC, animated: true)
// self.window? = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = navController
self.window?.makeKeyAndVisible()
completionHandler(true)
}
This code works, but you when I try to leave this VC, the app just sits there unresponsive. I some how need to embedded addPersonVC into the main Nav VC that is set up in storyboard. (The embedded navigation controller was set up in storyboard, incase that matters.)
Option1: Add a Storyboard Identifier to your UINavigationController , and instantiate the UINavigationController instead.
Option2: Delete the UINavigationController from storyboard. Just initialize a new UINavigationController by code and set the rootView programatically.
I am not a Swift developer, but since you don't see the examples I wrote I did some quick pasting to help you understand the basics, try this code:
let mainStoryboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let addPersonVC : AddPersonViewController = mainStoryboard.instantiateViewController(withIdentifier: "AddPerson") as! AddPersonViewController
// pass the stack to the addPersonVC
addPersonVC.coreDataStack = coreDataStack
let navController:UINavigationController = mainStoryboard.instantiateViewController(withIdentifier: "navController") as! UINavigationController
navController.viewControllers = [addPersonVC]
self.window?.rootViewController?.present(navController, animated: true, completion: nil)
self.window?.makeKeyAndVisible()

PresentViewController is showing nil of my RootViewController

So let me explain.
I have two ViewControllers (Login-Page and Main-Page).
Login page contains two buttons (FB and G+ login buttons).
Everything works fine with login credentials and is presenting the next view which is Main-Page.
In Main-Page I have button (sign-out).Button should work as singing out and bring me back to Login-Page, unfortunately is not working.
Below is my code
let vc = self.storyboard?.instantiateViewController(withIdentifier: "LoginPage") as! LoginPage
vc.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
self.present(vc, animated: true, completion: nil)
Is showing
fatal error: unexpectedly found nil while unwrapping an Optional value
and the output is nil for all declared IBOutlet in Main-Page
Please I need help!!!??
First we verify if the view controller has a Storyboard ID on the Identity inspector:
Then:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginViewController = storyboard.instantiateViewController(withIdentifier: "LoginPage")
let window = UIApplication.shared.windows[0] as UIWindow
UIView.transition(with: window,
duration: 0.5,
options: UIViewAnimationOptions.transitionCrossDissolve,
animations: {
window.rootViewController = loginViewController
}, completion: nil)
With that, you are replacing the presented view controller for your login view controller.
Try using notifications to change the root when you did logout for the app.
First in your appDelegate addObserver:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Notification used in logout action
let notf = NSNotificationCenter.defaultCenter()
notf.addObserverForName("logout", object: nil, queue: NSOperationQueue.mainQueue()) { (notification) in
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoginPage") as! LoginPage
self.window?.rootViewController = controller
}
return true
}
then in the logoutAction simply postNotification
func logout() {
NSNotificationCenter.defaultCenter().postNotificationName("logout", object: nil)
}

Issue Present View Controller with Custom URL Scheme

In Storyboard: NavigationController -> UITableViewController
appdelegate.swift
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let navVC = storyboard.instantiateViewControllerWithIdentifier("navViewController") as! UINavigationController
if let tableVC = navVC.topViewController as? UITableViewController {
tableVC.searchText = // URL query param
}
self.window?.rootViewController?.presentViewController(navVC, animated: false, completion: nil)
}
return true
}
The problem was when I enter with custom URL in Safari in second time
appUrl://search?q=...
The first time it is Ok, open correctly, but if I come back to Safari to open another URL I got
Warning: Attempt to present <UINavigationController: 0x7fb1fb090000> on <UINavigationController: 0x7fb1fa843400> whose view is not in the window hierarchy!
I think the problem is that the rootViewController tries to present two UIViewController which triggers the warning message.How about presenting the second UIViewController on the presented UIViewController.
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let navVC = storyboard.instantiateViewControllerWithIdentifier("navViewController") as! UINavigationController
if let tableVC = navVC.topViewController as? UITableViewController {
tableVC.searchText = // URL query param
}
self.window?.rootViewController?.presentedViewController?.presentViewController(navVC, animated: false, completion: nil)
}
return true
}
Try this
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let navVC = storyboard.instantiateViewControllerWithIdentifier("navViewController") as! UINavigationController
if let tableVC = navVC.topViewController as? UITableViewController
{
tableVC.searchText
}
if let presented = window?.rootViewController?.presentedViewController?.presentedViewController
{
presented.dismissViewControllerAnimated(false, completion: {
self.window?.rootViewController?.presentedViewController?.presentViewController(navVC, animated: false, completion: nil)
})
}
else
{
self.window?.rootViewController?.presentedViewController?.presentViewController(navVC, animated: false, completion: nil)
}
return true
}

Resources