navigation bar disappears when opening view controller via deep link - ios

I've used a tutorial to implement a method in app delegate to open specific view controllers from a HTML button from safari. my app delegate looks like this:
func application(_ app: UIApplication, open URL: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
print("url \(url)")
print("url host :\(url.host!)")
print("url path :\(url.path)")
let urlPath : String = (url.path as String?)!
let urlHost : String = (url.host as String?)!
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if(urlHost != "mywebsite.ir")
{
print("Host is not correct")
return false
}
if(urlPath == "/index"){
let innerPage: ViewController = mainStoryboard.instantiateViewController(withIdentifier: "HomeViewController") as! ViewController
self.window?.rootViewController = innerPage
}
self.window?.makeKeyAndVisible()
return true
}
and my HTML button reference is:
open app
but when my app home page opens, the navigation bar is gone and the user gets stuck and doesn't have any access to menu buttons or back button(in case of internal view controllers).
screenshots of home view controller before and after deep link:

Please update your code as below..you should have to put your navigation controller as a window root view controller..
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
print("url \(url)")
print("url host :\(url.host!)")
print("url path :\(url.path)")
let urlPath : String = (url.path as String?)!
let urlHost : String = (url.host as String?)!
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if(urlHost != "mywebsite.ir")
{
print("Host is not correct")
return false
}
if(urlPath == "/index"){
let innerPage: ViewController = mainStoryboard.instantiateViewController(withIdentifier: "HomeViewController") as! ViewController
let nav = UINavigationController.init(rootViewController: innerPage)
self.window?.rootViewController = nav
}
self.window?.makeKeyAndVisible()
return true

Does HomeViewController is the initial ViewController? If not, you could call the initial view controller that should be the view controller with a navigation controller.
Try to do this:
mainStoryboard.instantiateInitialViewController()
If the behaviour persists you could embed a NavigationController on code
let viewController = mainStoryboard.instantiateViewController(withIdentifier: "HomeViewController") as! ViewController
let navigationController = UINavigationController(rootViewController: viewController)
self.window?.rootViewController = navigationController
Also you don't need to call window.makeKeyAndVisible()

Related

Going from Navigation controller to tab controller views

I am developing the iOS app. and it has some design restrictions by the client. So it looks like I have to use navigation controller and the tab controller simultaneously on different number of Viewcontrollers.
I can categories my views into 2 category.
SignUp/Login views (this is entry point at first start of app)
MainView -> This become entry point once the user is login
Now Category 1 is using NavigationView Controllers. where as Category2 is using Tab bar controllers.
What I want case 1: I want when the user install my app, he is taken to Login view which has Navigation View. Now if he already has no account he will go to "Create new account" this is the 2nd scene of the Navigation view. Now on successful creation of account, he needs to close all other navigation view controllers and need to jump to MainView which will be Tabbar view controller.
case 2: suppose user close my app after getting login, when he open it up again, now the entery point will be Mainview which has Tab bar view controller. Now I know I need to do it in App delegate method but how?
I am doing this way,and it looks like working. But I am not getting bottom tab. Why is it so?
class ViewSwitcher {
static func updateRootViewController() {
let status = UserDefaults.standard.bool(forKey: KeyConstants.IS_USER_LOGGEDIN)
var rootViewController : UIViewController?
#if DEBUG
print(status)
#endif
if (status == true) {
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let mainTabBarController = mainStoryBoard.instantiateViewController(withIdentifier: "idTab1VC") as! Tab1VC
rootViewController = mainTabBarController
} else {
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let signInViewController = mainStoryBoard.instantiateViewController(withIdentifier: "idLoginVC") as! LoginVC
rootViewController = signInViewController
}
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = rootViewController
}
}
Any Idea how to do these cases? How to navigate programmatically by closing all the view controllers trees?
The same process I have been using for my app.
Use this code to change the rootViewController in Case2
var window: UIWindow?
var tabBarController : UITabBarController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Tabbar controller
let storyBoard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let tab1 = UINavigationController.init(rootViewController: storyBoard.instantiateViewController(withIdentifier: "tab1"))
tab1 = UITabBarItem.init(title: "Title 1", image: UIImage(named : "Image.png") , tag: 0)
let tab2 = UINavigationController.init(rootViewController: storyBoard.instantiateViewController(withIdentifier: "tab2"))
tab2 = UITabBarItem.init(title: "Title 2", image: UIImage(named : "Image.png") , tag: 1)
// Navigation controller or Login view controller
let nav1 = UINavigationController.init(rootViewController: storyBoard.instantiateViewController(withIdentifier: "nav1"))
tabBarController = UITabBarController.init()
tabBarController?.delegate = self
tabBarController?.selectedIndex = 0
tabBarController?.viewControllers = [tab1,tab2]
// The Bool value which you have to set as True after a user logged in
if UserDefaults.standard.bool(forKey: "LoggedIn"){
print("Tabbar")
self.window?.rootViewController = self.tabBarController
}else{
print("Navigation")
self.window?.rootViewController = nav1
}
return true
}
Before or After that, you have to write the same code where a user clicked on login in your loginViewController, in your case it is Case1
func loginClicked(){
UserDefaults.standard.set(true, forKey: "LoggedIn")
DispatchQueue.main.async {
let appdelegate = UIApplication.shared.delegate as! AppDelegate
// Same as above code and replace self with appDelegate without if condition and at last
appdelegate.window?.rootViewController = appdelegate.tabBarController
}
}
And after user logged out
DispatchQueue.main.async {
UserDefaults.standard.set(false, forKey: "LoggedIn")
let appdelegate = UIApplication.shared.delegate as! AppDelegate
let story = UIStoryboard.init(name: "Main", bundle: Bundle.main)
let nav1 = UINavigationController.init(rootViewController: storyBoard.instantiateViewController(withIdentifier: "nav1"))
appdelegate.window?.rootViewController = companyNavigation
}
Use below code in app delegate didFinishWithLaunchingOptions , for main view directly.. here uid means stored value whether to check user is Active or Not.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let user_id = UserDefaults.standard.value(forKey: "uid")
{
let homeViewController = storyboard.instantiateViewController(withIdentifier: "HomeTabBarCtrlr") as! UITabBarController
let nav = UINavigationController(rootViewController: homeViewController)
self.window!.rootViewController = nav
}else{
SignUp/Login views
}
Step by step approach to this would be:
Check if user is logged in or not. For this use UserDefaults.
If user not logged in, set LoginVC as rootVC.
On Login save a key in UserDefault to signify user logged in.
Next time when user opens the app check for key and if user logged in, set TabBarVC as rootViewController.
At Logout, clear the key.
Sample Code for the above will be:
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
{
...........
//Check for user login and set rootViewController.
if UserDefaults.standard.bool(forKey: "login"){
self.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
}
else{
self.window?.rootViewController = UIStoryboard(name: "Login", bundle: nil).instantiateInitialViewController()
}
return true
}
At Login Success
func loginUser(){
...
if userLoginSuccess{
UserDefaults.standard.set(true, forKey: "login")
}
}
At Logout
UserDefaults.standard.set(false, forKey: "login")
As like you know that you want to do it in Appdelgate.
When user login successfully you have to store a flag in userdefault.
In app delegate you have to check that if this flag is set or not.If set then create a navigation view controller with your initial view controller call moveToLoginWindow().
or if not then create tab bar view controller call movetoTabBarController().
func moveToLoginWindow() -> Void {
if let rootController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LoginVC") as? LoginVC{
let navigationController = UINavigationController.init(rootViewController: rootController)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
}
func movetoTabBarController() -> Void {
let nav2 = UINavigationController()
let second = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "secondViewController") as? SecureNotesVC
nav2.viewControllers = [second!]
nav2.tabBarItem = UITabBarItem.init(title: "Titel 2", image: UIImage.init(named: "2.png"), selectedImage: UIImage.init(named: "2_sel.png"))
let nav3 = UINavigationController()
let third = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "thirdviewController") as? FormFillsVC
nav3.viewControllers = [third!]
nav3.tabBarItem = UITabBarItem.init(title: "Titel 2", image: UIImage.init(named: "3.png"), selectedImage: UIImage.init(named: "3_sel.png"))
let nav4 = UINavigationController()
let fourth = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "fourthviewContoller") as? SettingsVC
nav4.viewControllers = [fourth!]
nav4.tabBarItem = UITabBarItem.init(title: "4", image: UIImage.init(named: "4.png"), selectedImage: UIImage.init(named: "4_sel.png"))
if #available(iOS 11.0, *) {
nav2.navigationBar.prefersLargeTitles = true
nav3.navigationBar.prefersLargeTitles = true
nav4.navigationBar.prefersLargeTitles = true
}
let tabBarController = UITabBarController()
tabBarController.viewControllers = [ nav2,nav3, nav4,nav5]
self.window!.rootViewController = tabBarController;
}

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);
}
}
}

Load A View Controller programmatically from a UIView

I am using the following from within a class to call a UIViewController. The UIViewController loads, but then becomes unresponsive after loading.
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let homeC = storyboard.instantiateViewController(withIdentifier:
"ViewJoeProfileController") as? ViewJoeProfileController
if homeC != nil {
homeC!.view.frame = (self.window!.frame)
self.window!.addSubview(homeC!.view)
self.window!.bringSubview(toFront: homeC!.view)
}
}
Any suggestions to make the UIViewController being loaded responsive would really help!
If you specifically want to add to the window, you should do it the proper way and add the whole ViewController:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
guard let window = UIWindow(frame: UIScreen.mainScreen().bounds) else { return true }
if let homeC = storyboard.instantiateViewController(withIdentifier: "ViewJoeProfileController") as? ViewJoeProfileController {
window.rootViewController = mainViewController
window.makeKeyAndVisible()
}
return true
}
But honestly that structure still doesn't seem correct. Why are you adding a view to the window, instead of using an initial view controller and then adding to itsviews, or segueing to different ViewControllers?.
Try using this I need to make root View Controller
let storyBoard = UIStoryboard.init(name: "Main", bundle: nil)
let vc = storyBoard.instantiateViewController(withIdentifier: "ViewJoeProfileController") as! ViewJoeProfileController
let nav = UINavigationController(rootViewController: vc)
nav.isNavigationBarHidden = true
self.window?.rootViewController=nav
self.window?.makeKeyAndVisible()

Saving VC on exit (IOS)

I have the code to save VC on exit.
It works but I use SWRevealViewController but the navigation controller in the application is not displayed
How can I correct it?
ViewController.swift
super.viewDidLoad()
NSUserDefaults.standardUserDefaults().setInteger(0, forKey: "View")
ViewController2.swift
super.viewDidLoad()
NSUserDefaults.standardUserDefaults().setInteger(1, forKey: "View")
AppDelegate.swift
let viewCount = UserDefaults.standard.integer(forKey: "View")
var VC = UIViewController()
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
print(viewCount)
if viewCount == 0 {
VC = storyboard.instantiateViewController(withIdentifier: "First") as! ViewController
} else if viewCount == 1 {
VC = storyboard.instantiateViewController(withIdentifier: "Second") as! ViewController2
}
self.window?.makeKeyAndVisible()
self.window?.rootViewController = VC
return true
Error
My StoryBoard
So I think the issue is that you are setting the root view controller of the window to be a regular view controller. What you need to do is set this to be a navigation controller with your embedded view controllers. I'm not sure of your exact setup with SWRevealViewController but I guess you need something like this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let viewCount = UserDefaults.standard.integer(forKey: "View")
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let VC = storyboard.instantiateViewController(withIdentifier: "First") as! ViewController
let frontNavigationController = UINavigationController(rootViewController: VC)
let VC2 = storyboard.instantiateViewController(withIdentifier: "TableView") as! TableView
let rearNavigationController = UINavigationController(rootViewController: VC2)
let revealVC = SWRevealViewController(rearViewController: rearNavigationController, frontViewController: frontNavigationController)
revealVC.delegate = self
self.window?.rootViewController = nil
self.window?.rootViewController = revealVC
self.window?.makeKeyAndVisible()
return true
}
If this isn't working, or what you are looking to achieve, please ask in comments.
EDIT:
So, if you want to display the TableView menu on the left, you have to set the storyboard id of it to be TableView (obvs set this in the storyboard). And then the code above should work.
I'm not entirely sure why you are using UserDefaults to save the VC. But you can now use the TableView class to navigate between VCs. If you want to save the last visited VC and display this on application finished launching, you would need this:
var string = ""
switch viewCount {
case 0:
string = "First"
case 1:
string = "Second"
case 2:
string = "Third"
case 3:
string = "Fourth"
case 4:
string = "Fifth"
default:
string = "First"
}
let VC = storyboard.instantiateViewController(withIdentifier: string)
let frontNavigationController = UINavigationController(rootViewController: VC)
Just make sure you have set the storyboard id of each VC to be "First", "Second", "Third" and so on. This would replace the previous declarations of let VC and let frontNavigationController in my above answer.
If in your storyboard your initial ViewController is UINavigationViewController then you can access this navigation controller in AppDelegate's didFinishLaunchingWithOptions as follows:
let navigationController = application.windows[0].rootViewController as! UINavigationController
Also, there is no need to track/save your view controllers in UserDefaults, you can always access the list of ViewControllers like this
AppDelegate
let childViewControllers = window?.rootViewController?.childViewControllers
print("childVC: \(String(describing: childViewControllers))")
In Any other ViewController
let childViewControllers = UIApplication.shared.keyWindow?.rootViewController?.childViewControllers
print("vcs : \(String(describing: childViewControllers))")
And of course, you can compare childViewControllers list
if let childViewControllers = UIApplication.shared.keyWindow?.rootViewController?.childViewControllers {
for viewController in childViewControllers {
if viewController is FirstViewController {
print("firstViewController found...")
}
if viewController is SecondViewController {
print("secondViewController found...")
}
}
}
In case if you're setting SWRevealViewController for side-menu using storyboard then the basic setup should be like
For more detail on SWRevealViewController have a look at their GitHub documentation, check sample project and mentioned tutorials here for deep understanding. Hope it helps.

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()

Resources