how to use navigation controller programaticaly? - ios

i have a button in viewcontroller which navigates to editPage viewcontrooler
i want to use pushNavigation
i did below code in button action method
#IBAction func editBtn(_ sender: Any) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "editProfile") as! editProfile
let navigation = UINavigationController(rootViewController: newViewController)
self.navigationController?.pushViewController(navigation, animated: true)
}
but when i click on button it dosent navigate
i dont have navigation controler in storyboard.......
i dont want to present view controler

Looks like you are setting your viewController as initail View Controller from storyboard but do not embed this in navigation controller , either embed your viewController in navigationController and set this navigation controller as initialViewController or follow this approach as you don't want to do it from storyboard:
In SceneDelegate , write :
var window: UIWindow?
and then in this method :
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
//Setup user state
if let vc = UIStoryboard.init(name: "YOUR_STORYBOARD_NAME", bundle: nil).instantiateViewController(withIdentifier: "VIEW_CONTROLLER_IDENTIFIER") as? ViewController {
let nav = UINavigationController(rootViewController: vc)
window?.rootViewController = nav
window?.makeKeyAndVisible()
}
}
for IOS < 13, in AppDelegate.swift :
var window : UIWindow?
//MARK:- DID FINISH LAUNCHING WITH OPTIONS : CALLED EVERYTIME WHEN APPLICATION LAUNCHES
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let vc = UIStoryboard.init(name: "YOUR_STORYBOARD_NAME", bundle: nil).instantiateViewController(withIdentifier: "VIEW_CONTROLLER_IDENTIFIER") as? ViewController {
let nav = UINavigationController(rootViewController: vc)
window?.rootViewController = nav
window?.makeKeyAndVisible()
}
return true
}

Related

Programmatically embedding in navigation controller (Xcode 14.2, io2 16+)

I had a working program consisting basically of a table view controller embedded in a navigation controller, and I decided to try to get rid to some mysterious/obnoxious warnings that first appeared in iOS 16 ("UINavigationBar decoded as unlocked for UINavigationController, or navigationBar delegate set up incorrectly"). Following online suggestions, I got rid of the navigation controller on the storyboard, and added some code to
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window?.windowScene = windowScene
window?.makeKeyAndVisible()
// ViewController: UITableViewController is my VC class
let viewController = ViewController()
let navViewController = UINavigationController(rootViewController: viewController)
window?.rootViewController = navViewController
}
to embed it programmatically. The program would then crash with a message that stating that it could not deque a cell with identifier "itemCell", and "must register a nib or a class for the identifier or connect a prototype cell in a storyboard". I solved that by adding the line
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "itemCell")
in ViewDidLoad but I don't understand why this was necessary, since the identifier for the prototype cell is still there in the storyboard, just as it was before. Can someone illuminate me? Thanks.
This:
let viewController = ViewController()
let navViewController = UINavigationController(rootViewController: viewController)
window?.rootViewController = navViewController
won't give you what you want.
In Storyboard, you have to give ViewController a Storyboard ID (such as "FirstViewController"), then instantiate it via code:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewController(withIdentifier: "FirstViewController")
let navViewController = UINavigationController(rootViewController: initialViewController)
Edit
Assuming the view controller we ID as "FirstViewController" in Storyboard is a table view controller, and we want to load it as the "root" controller for a navigation controller, the full code block would be:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window?.windowScene = windowScene
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewController(withIdentifier: "FirstViewController")
let navViewController = UINavigationController(rootViewController: initialViewController)
window?.rootViewController = navViewController
window?.makeKeyAndVisible()
}

Trouble Calling A Tab Bar Controller From App Delegate

I am having trouble launching a tab bar controller from App Delegate. However, the controller is called correctly from a different view controller, so I know it's working. This is the code I used in the view controller:
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
self.window = UIWindow(frame: UIScreen.main.bounds)
let tabBarController = self.storyboard?.instantiateViewController(identifier: Constants.Storyboard.tabBarController)
self.view.window?.rootViewController = tabBarController
self.view.window?.makeKeyAndVisible()
While that worked for the view controller, that didn't work for App Delegate, so after researching I found the following:
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
self.window = UIWindow(frame: UIScreen.main.bounds)
let tabBarController = storyBoard.instantiateViewController(withIdentifier: Constants.Storyboard.tabBarController)
self.window?.rootViewController = tabBarController
self.window?.makeKeyAndVisible()
That didn't work either. Any ideas on what I'm doing wrong? If it matters, please note the tab bar controller doesn't have its own class, I designed it in Storyboard.
Thanks for any help.
I think you should move your code to the SceneDelegate and do some changes concerning the initializiation of window.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = storyBoard.instantiateViewController(withIdentifier: Constants.Storyboard.tabBarController)
window = UIWindow(windowScene: windowScene)
self.window?.rootViewController = tabBarController
self.window?.makeKeyAndVisible()
}

Swift: Two View Controllers presented

Apologies for the ambiguity of the question; I'll try to explain clearer here.
The Problem
I have some code in AppDelegate.swift that performs conditional navigation; if the user is signed in (and therefore currentUser is not nil), the app will start in View, a View Controller in the Main storyboard. Else, if the user isn't signed in, the app will start in Welcome, a View Controller in an Auth storyboard.
The following is my code from AppDelegate.swift, ViewController.swift (which is View), and WelcomeViewController.
Note:
View is the Storyboard ID I set for ViewController; Welcome is the Storyboard ID I set for WelcomeViewController.
AppDelegate.swift
import UIKit
import Firebase
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Configures the Firebase library.
FirebaseApp.configure()
// Performs conditional navigation on the condition of currentUser's status.
self.window = UIWindow(frame: UIScreen.main.bounds)
let user = Auth.auth().currentUser
if user != nil {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootViewController = storyboard.instantiateViewController(identifier: "View")
window?.rootViewController = rootViewController
UIView.animate(withDuration: 0.5) {
self.window?.makeKeyAndVisible()
}
} else {
let storyboard = UIStoryboard(name: "Auth", bundle: nil)
let rootViewController = storyboard.instantiateViewController(identifier: "Welcome")
window?.rootViewController = rootViewController
UIView.animate(withDuration: 0.5) {
self.window?.makeKeyAndVisible()
}
}
return true
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print("ViewController presented.")
}
}
WelcomeViewController.swift
import UIKit
class WelcomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print("WelcomeViewController presented.")
}
}
Expectations
The app is freshly installed on the Simulator, and therefore there should be no currentUser (and upon debugging, proves to be true). Therefore, I'd expect to see this as the output in the Console:
WelcomeViewController presented.
Instead, this shows up in the Console:
WelcomeViewController presented.
ViewController presented.
Attempts
So far, I thought that the problem might have arised from the fact that Main storyboard is the default Storyboard set since the creation of the project. Therefore, I tried unchecking the 'Initial View Controller' checkbox for the two View Controllers. Just like I'd expected, the following appears instead:
WelcomeViewController presented.
2020-06-20 11:39:38.724658+0800 AppName[11439:237509] [WindowScene] Failed to instantiate the default view controller for UIMainStoryboardFile 'Main' - perhaps the designated entry point is not set?
What should I do to make sure that ViewController doesn't appear and replace WelcomeViewController? All help is appreciated, thanks!
why not set WelcomeViewController as your 'Initial View Controller' and replace its viewDidLoad code with AppDelegate file then:
class WelcomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print("WelcomeViewController presented.")
let user = Auth.auth().currentUser
if user != nil {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootViewController = storyboard.instantiateViewController(identifier: "View")
window?.rootViewController = rootViewController
UIView.animate(withDuration: 0.5) {
self.window?.makeKeyAndVisible()
}
}
}
}
you can also look this if you want to set the view controller from AppDelegate:
EDIT:
you can use a UINavigationController as your initial view controller as indicated in this answer:
set initial viewcontroller in appdelegate - swift
then you just need to set the correct view controller here:
navigationController.viewControllers = [rootViewController]
self.window?.rootViewController = navigationController
Solution
I managed to find the solution to the issue that I'm facing here. There are two mistakes above in the code, and I'll explain it here:
Mistake 1: Code should be in SceneDelegate instead of AppDelegate (iOS 13)
Xcode 11, along with the release of iOS 13, introduced SceneDelegate.swift. I was supposed to put my code above in SceneDelegate.swift instead of AppDelegate.swift; because I didn't do this, the code hadn't worked as I'd hoped for my Simulator, which was running iOS 13.5.
Here's my rectified code in SceneDelegate.swift:
import UIKit
import Firebase
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
// Performs conditional navigation on the condition of currentUser's status.
let user = Auth.auth().currentUser
if user != nil {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootViewController = storyboard.instantiateViewController(identifier: "View")
window?.rootViewController = rootViewController
UIView.animate(withDuration: 0.5) {
self.window?.makeKeyAndVisible()
}
} else {
let storyboard = UIStoryboard(name: "Auth", bundle: nil)
let rootViewController = storyboard.instantiateViewController(identifier: "Welcome")
window?.rootViewController = rootViewController
UIView.animate(withDuration: 0.5) {
self.window?.makeKeyAndVisible()
}
}
}
}
Mistake 2 (Important!): Definition of new UIWindow
In my code above, there was a faulty line which caused the creation of a new UIWindow:
self.window = UIWindow(frame: UIScreen.main.bounds)
By removing this line of code, the problem was resolved. Here's the rectified code:
let user = Auth.auth().currentUser
if user != nil {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootViewController = storyboard.instantiateViewController(identifier: "View")
window?.rootViewController = rootViewController
UIView.animate(withDuration: 0.5) {
self.window?.makeKeyAndVisible()
}
} else {
let storyboard = UIStoryboard(name: "Auth", bundle: nil)
let rootViewController = storyboard.instantiateViewController(identifier: "Welcome")
window?.rootViewController = rootViewController
UIView.animate(withDuration: 0.5) {
self.window?.makeKeyAndVisible()
}
}
I hope this helps anyone else facing this issue!

Setting root view controller upon launch, depending on user auth state

This is the storyboard layout I have right now
So what I need is to check if the user is already authenticated when the app launches.
If he is not logged in set the root view controller to the navigation controller with the login and register forms.
If he is logged in set it to the tab bar view controller.
I tried a lot of different solutions but none of them worked. It just kept setting the view controller to the one marked with "Is Initial View Controller".
This is the code I tried in the AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
var viewController: UIViewController
if isAuthenticated() {
viewController = storyboard.instantiateViewController(withIdentifier: Constants.Storyboards.homeViewController) as! UITabBarController
} else {
viewController = storyboard.instantiateViewController(withIdentifier: Constants.Storyboards.authViewController) as! UINavigationController
}
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
return true
}
I can do this by pressing a button easily, but I want it to happen without the user needing to do something.
EDIT: Thanks to #LukaCefarin and #Francesco Deliro I managed to find out that the problem was. I was using XCode 11 and the rootViewController had to be set in the SceneDelegate.swift
This is what my code in the SceneDelegate.swift looks like for anyone who has a similar issue:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: scene as! UIWindowScene)
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
var viewController: UIViewController;
if isAuthenticated() {
viewController = storyboard.instantiateViewController(withIdentifier: "HomeVC")
} else {
viewController = storyboard.instantiateViewController(withIdentifier: "AuthVC")
}
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
}
EDIT:
This solution and setting the window rootViewController in the AppDelegate work for versions prior to Xcode11 and iOS13. As suggested in the comments by Luka Cefarin, if you are using Xcode11 and iOS13 you have to set the window rootViewController in the SceneDelegate.swift file.
You have to remove the Main Interface and to uncheck the initial view controller in the storyboard:
Before
After

How to programmatically add a second view in swift 3

Im trying to switch to a second view from a button on the first view, and do this programmatically (not using the traditional way of switching between views using a segue on the storyboard)
My current app delegate:
import UIKit
#UIApplicationMain
public class AppDelegate: UIResponder, UIApplicationDelegate {
public var window: UIWindow?
public func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = UINavigationController(rootViewController: ViewController())
return true
}
Button function in first view controller that when pressed should just switch to new view:
func signupAction (sender: UIButton!){
}
I already have the new viewcontroller file created:
class Signup: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.cyan
}
}
Basically, I'm trying to achieve the button in the first viewcontroller transitions to this signup viewcontroller.
If you are in a UINavigationController you could push the Signup view controller to the navigation stack this way:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let signUpViewController = storyboard.instantiateViewController(withIdentifier: "signUpViewController") as! Signup
navigationController?.pushViewController(signUpViewController, animated: true)
If you are not in UINavigationController and want to present it, you can do it this way:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let signUpViewController = storyboard.instantiateViewController(withIdentifier: "signUpViewController") as! Signup
self.present(signUpViewController, animated: true, completion: nil)
Note: Don't forget to set the identifier of your view controller in the storyboard.

Resources