I created the structure as below in xcode 8 swift 3.0.
Before I add AppDelegate code. Back button still appear fine on Apply, Apply Behalf and Profile controller.
I use segue to open page.
But after I add AppDelegate code into Homepage and Login controllers , back button disappears on Apply, Apply behalf and profile controller page.
Can someone help or explain why is this happening ? How to enable back the back button on apply, apply behalf and profile page ?
Thanks.
Home.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet var staffNumberLbl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
staffNumberLbl.text = UserDefaults.standard.object(forKey: "login") as? String
}
override func viewDidAppear(_ animated: Bool) {
let isUserLoggedIn = UserDefaults.standard.bool(forKey: "loggedIn")
if(!isUserLoggedIn){
self.performSegue(withIdentifier: "loginview", sender: self)
}
}
#IBAction func logoutData(_ sender: Any) {
UserDefaults.standard.set(false, forKey: "loggedIn")
UserDefaults.standard.synchronize();
let loginViewController = self.storyboard!.instantiateViewController(withIdentifier: "loginview") as! LoginViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = loginViewController
appDelegate.window?.makeKeyAndVisible()
}
}
Login.swift
import UIKit
class LoginViewController: UIViewController {
#IBOutlet var loginlbl: UITextField!
#IBOutlet var passlbl: UITextField!
#IBOutlet var login_button: UIButton!
var login: String!
var pw: String!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func loginData(_ sender: Any) {
login = loginLbl.text
pw = passLbl.text
if(login == "" || pw == ""){
return
}
else{
let url = URL(string: "http://localhost/login.php")
let session = URLSession.shared
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "POST"
let LoginDataToPost = "login=\(login!)&pw=\(pw!)"
request.httpBody = LoginDataToPost.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) in
if error != nil {
return
}
else {
do {
if let json = try JSONSerialization.jsonObject(with: data!) as? [String: String]
{
DispatchQueue.main.async
{
let message = Int(json["message"]!)
let login = json["login"]
if(message == 1) {
UserDefaults.standard.set(true, forKey: "isUserLoggedIn")
UserDefaults.standard.set(login, forKey: "login")
UserDefaults.standard.synchronize();
self.dismiss(animated: true, completion: nil)
let myViewController:ViewController = self.storyboard!.instantiateViewController(withIdentifier: "ViewController") as! ViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = myViewController
appDelegate.window?.makeKeyAndVisible()
print("Value for login is : \(login!)")
return
}
else {}
}
}
else {}
}
catch let jsonParse {}
}
})
task.resume()
}
}
}
AppDelegate.swift
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let mainStoryBoard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let isUserLoggedIn:Bool = UserDefaults.standard.bool(forKey: "isUserLoggedIn")
if(!isUserLoggedIn) {
let loginViewController = mainStoryBoard.instantiateViewController(withIdentifier: "loginview") as! LoginViewController
window!.rootViewController = loginViewController
window!.makeKeyAndVisible()
}
else {
let homePage = mainStoryBoard.instantiateViewController(withIdentifier: "ViewController") as! ViewController
window!.rootViewController = homePage
window!.makeKeyAndVisible()
}
return true
}
}
You are setting rootviewcontroller without embedding navigation controller to it in logoutData & loginData function.
Use this code :
let navigationController = UINavigationController.init(rootViewController: myViewController)
appDelegate.window?.rootViewController = navigationController
Use this code in AppDelegate:
if(!isUserLoggedIn) {
let loginViewController = mainStoryBoard.instantiateViewController(withIdentifier: "loginview") as! LoginViewController
let navigationController = UINavigationController.init(rootViewController: loginViewController)
appDelegate.window?.rootViewController = navigationController
window!.makeKeyAndVisible()
}
else {
let homePage = mainStoryBoard.instantiateViewController(withIdentifier: "ViewController") as! ViewController
let navigationController = UINavigationController.init(rootViewController: homePage)
appDelegate.window?.rootViewController = navigationController
window!.makeKeyAndVisible()
}
Remove this from Home.swift,
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = loginViewController
appDelegate.window?.makeKeyAndVisible()
because its not inheriting the properties of Navigation controller
and add it in Appdelegate.swift file
For the other 3 viewcontrollers, you need to add the Navigation controller between eachSegway in order to inherit it or code the button by instantiating the viewcontrollers respectively
After successful login,try to make NavigationController as rootViewController instead of your ViewController
Your back button will start appearing.
In AppDelegate, in else block, instead of this line
let homePage = mainStoryBoard.instantiateViewController(withIdentifier: "ViewController") as! ViewController
write this
let homePage = mainStoryBoard.instantiateViewController(withIdentifier: "NavigationController") as! UINavigationController
Inside LoginViewController, in the block if(message == 1)
replace
let myViewController:ViewController = self.storyboard!.instantiateViewController(withIdentifier: "ViewController") as! ViewController
with
let navController:UINavigationController = self.storyboard!.instantiateViewController(withIdentifier: "NavigationController") as! UINavigationController
Also set storyboard identifier for UINavigationController in storyboard to NavigationController
Depending on your configuration:
self.navigationItem.hidesBackButton = YES;
OR:
self.navigationController.navigationItem.hidesBackButton = YES;
Or, if you just want to disable the button without hiding it, you can use this.
self.navigationController.navigationItem.backBarButtonItem.enabled = NO;
Related
I've strange bug I cannot understand how to solve. When I set which ViewController should be opened depends if users is registered and I put this code in SceneDelegate I get black screen for a moment before appearing ViewController
import UIKit
import FirebaseAuth
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
if let user = Auth.auth().currentUser {
FirestoreService.shared.getUserData(user: user) { (result) in
switch result {
case .success(let muser):
let navigationController = self.storyBoard.instantiateViewController(withIdentifier: "Navigation") as! UINavigationController
let conroller = self.storyBoard.instantiateViewController(withIdentifier: "MainController") as! MainController
navigationController.viewControllers = [conroller]
self.window?.rootViewController = navigationController
case .failure(_):
let conroller = self.storyBoard.instantiateViewController(withIdentifier: "SignUpController") as! SignUpController
self.window?.rootViewController = conroller
}
}
} else {
print(3)
let conroller = storyBoard.instantiateViewController(withIdentifier: "SignUpController") as! SignUpController
self.window?.rootViewController = conroller
}
window?.makeKeyAndVisible()
}
But when I put code for appearing ViewController outside the firebase it works as it should. How this can be fixed?
I think there are 2 ways to solve this issue.
1. Using a splash screen:
Don't make the decision of showing a login screen or main screen in scene(_:, session, connectionOptions). Instead, create a new view controller called SplashScreenViewController. Set your app's logo in its center. And then in its viewDidLoad() move your logic to check if the user is already logged in:
func chooseAndPresentStartScreen() {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
if let user = Auth.auth().currentUser {
FirestoreService.shared.getUserData(user: user) { (result) in
switch result {
case .success(let muser):
let navigationController = storyBoard.instantiateViewController(withIdentifier: "Navigation") as! UINavigationController
let conroller = storyBoard.instantiateViewController(withIdentifier: "MainController") as! MainController
navigationController.viewControllers = [conroller]
self.present(navigationController, animated: true, completion: nil)
case .failure(_):
let conroller = storyBoard.instantiateViewController(withIdentifier: "SignUpController") as! SignUpController
self.present(conroller, animated: true, completion: nil)
}
}
} else {
print(3)
let conroller = storyBoard.instantiateViewController(withIdentifier: "SignUpController") as! SignUpController
self.present(conroller, animated: true, completion: nil)
}
}
2. Using User Defaults:
Define a global variable for the key used to save user default value.
let isUserLoggedInKey: String = "IsUserLoggedIn"
After the user logs-in:
let defaults = UserDefaults.standard
defaults.setValue(true, forKey: isUserLoggedInKey)
Then in your scene(_:, session, connectionOptions), check if this value is set.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
let defaults = UserDefaults.standard
let isLoggedIn = defaults.bool(forKey: isUserLoggedInKey)
if isLoggedIn {
let navigationController = self.storyBoard.instantiateViewController(withIdentifier: "Navigation") as! UINavigationController
let conroller = self.storyBoard.instantiateViewController(withIdentifier: "MainController") as! MainController
navigationController.viewControllers = [conroller]
self.window?.rootViewController = navigationController
} else {
let conroller = self.storyBoard.instantiateViewController(withIdentifier: "SignUpController") as! SignUpController
self.window?.rootViewController = conroller
}
window?.makeKeyAndVisible()
}
Don't forget to set isLoggedInKey to false when the user logs out.
let defaults = UserDefaults.standard
defaults.setValue(false, forKey: isUserLoggedInKey)
If you don't want to manually save value for isUserLoggedInKey when the user logs in and logs out, you can also use a state change listener. It takes a callback that is triggered when the user logs in or logs out. Add this in your Login view controller:
Auth.auth().addStateDidChangeListener { (auth, user) in
let defaults = UserDefaults.standard
defaults.setValue(user != nil, forKey: isUserLoggedInKey)
}
In appdelegate i am getting UserId.. means i am login but when i run second time i am not getting homeviewcontroller as rootviewcontroller why? still it shows phonenumviewcontroller
navigationcontroller -> phonenumviewcontroller -> registrationiewcontroller -> homeviewcontroller
In storyboard navigationcontroller is initialviewcontroller
In registrationviewcontroller i am getting userId which i have saved in keychain.
I dont have signout button so i have written code like below in registrationiewcontroller
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String:AnyObject]
print("terms and condition JSON \(json)")
let jsonObj: String = json["Success"] as? String ?? ""
if jsonObj == "true" {
let userID: String=jsonObj?["userId"] as? String ?? ""
DispatchQueue.main.async {
KeychainWrapper.standard.set(userID, forKey: "USERID")
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController;
UserDefaults.standard.set("NoLogout", forKey:"Signout")
self.navigationController?.pushViewController(viewController, animated: true);
}
}
}
no signout button so added this code in registrationviewcontroller
UserDefaults.standard.set("NoLogout", forKey:"Signout")
this code in appdelegate: getting userId but still homeviewcontroller is not coming as rootviewcontroller, only phonenumviewcontroller is coming why?
var savedUserId: String?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
savedUserId = KeychainWrapper.standard.string(forKey: "USERID")
KeychainWrapper.standard.set(savedUserId ?? "", forKey: "PersonalID")
print("appdelegate userid \(savedUserId)")
logOutString = UserDefaults.standard.string(forKey: "Signout") as NSString? ?? "" as NSString
if logOutString.isEqual(to: "NoLogout") {
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyBoard = UIStoryboard.init(name: "Main", bundle: nil)
let viewcontroller = storyBoard.instantiateViewController(withIdentifier: "HomeViewController")
let navigationController = UINavigationController(rootViewController: viewcontroller)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
else {
}
return true
}
once registration is completed i need rootviewcontroller as homeviewcontroller... how to get that, please help me with code
From whatever I've gathered, here's what you need to do:
In AppDelegate:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let isUserLoggedIn = UserDefaults.standard.bool(forKey: "isUserLoggedIn") // Give you own check to see if user is logged in
window = UIWindow()
window?.makeKeyAndVisible()
let viewController = isUserLoggedIn ? MainViewController() : LoginViewController()
window?.rootViewController = UINavigationController(rootViewController: viewController)
return true
}
}
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Logout", style: .plain, target: self, action: #selector(handleUserLoggedOut))
}
#objc func handleUserLoggedOut() {
UserDefaults.standard.set(false, forKey: "isUserLoggedIn")
UIApplication.shared.keyWindow?.rootViewController = UINavigationController(rootViewController: LoginViewController())
}
}
class LoginViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Login", style: .plain, target: self, action: #selector(handleUserLoggedIn))
}
#objc func handleUserLoggedIn() {
UserDefaults.standard.set(true, forKey: "isUserLoggedIn")
UIApplication.shared.keyWindow?.rootViewController = UINavigationController(rootViewController: MainViewController())
}
}
This is only a skeleton. Give both controller different background colours and button action to call handleUserLoginIn and handleUserLoggedOut on respective controllers and see how it works. Play around with it and figure out.
Note: keyWindow is deprecated so you need to use this instead at all places.
let keyWindow = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first
Edit: I've also added the login, logout buttons and set backgroundColors for you to see for yourself the result.
I am creating an application with Swift 3.0 and Xcode 8.2.1. On the main screen you have the Google map in the background and you also have a "NavigationDrawer Acvtiviy". The problem is that when I make a slide to the right or left, the "side" does not appear but the map moves. How can I distinguish between moving the map and showing the right or left side?. My code en UIViewController is:
class MainMapVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let camera = GMSCameraPosition.camera(withLatitude:40.4893538, longitude: -3.6827461, zoom: 5.5)
let mapView = GMSMapView.map(withFrame: .zero, camera: camera)
mapView.isMyLocationEnabled = true
mapView.mapType = .satellite
self.view = mapView
}
#IBAction func leftSideButtonTapped(_ sender: Any) {
let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.centerContainer?.toggle(MMDrawerSide.left,
animated:true, completion:nil)
}
#IBAction func rightSideButtonTapped(_ sender: Any) {
let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.centerContainer?.toggle(MMDrawerSide.right,animated:true, completion:nil)
}
}
And the code in AppDelegate is:
import UIKit
import GoogleMaps
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var centerContainer: MMDrawerController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
GMSServices.provideAPIKey("edcer..erc..er.erc.e.c.")
// Override point for customization after application launch.var rootViewController = self.window!.rootViewControllerlet mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let rootViewController = self.window!.rootViewController!
let mainStoryboard: UIStoryboard = UIStoryboard(name: "MainMap", bundle: nil)
let centerViewController = mainStoryboard.instantiateViewController(withIdentifier: "MainMap") as! MainMapVC
let leftViewController = mainStoryboard.instantiateViewController(withIdentifier: "LeftSideViewController") as! LeftSideViewController
let rightViewController = mainStoryboard.instantiateViewController(withIdentifier: "RightSideViewController") as! RightSideViewController
let leftSideNav = UINavigationController(rootViewController: leftViewController)
let centerNav = UINavigationController(rootViewController: centerViewController)
let rightNav = UINavigationController(rootViewController: rightViewController)
centerContainer = MMDrawerController(center: centerNav, leftDrawerViewController: leftSideNav,rightDrawerViewController:rightNav)
centerContainer!.openDrawerGestureModeMask = MMOpenDrawerGestureMode.panningCenterView;
centerContainer!.closeDrawerGestureModeMask = MMCloseDrawerGestureMode.panningCenterView;
window!.rootViewController = centerContainer
window!.makeKeyAndVisible()
return true
}
Check with following one
mapView.settings.consumesGesturesInView = false
When ViewController loads, the correct value gets printed. But when tried to print the same value with a UIButton, the print is nil
ViewController
var userEmail: String?
func userLoggedIn(data: String) {
userEmail = data //sent from delagate
print(userEmail) // successfully printed
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "loginView" {
let loginViewController: LoginViewController = segue.destination as! LoginViewController
loginViewController.delegate = self
}
}
override func viewDidAppear(_ animated: Bool) {
let isUserLoggedIn = UserDefaults.bool(UserDefaults.standard)(forKey: "isUserLoggedIn")
if(!isUserLoggedIn) {
self.performSegue(withIdentifier: "loginView", sender: self);
}
}
#IBAction func createCommunityTapped(_ sender: AnyObject) {
let createCommunityController = self.storyboard?.instantiateViewController(withIdentifier: "CreateNewCommunity") as! CreateNewCommunity
print ("now here ", userEmail) // prints nil
createCommunityController.myEmail = userEmail
}
The userEmail value is being passed from LoginViewController via this section of code:
if(returnValue != "error") {
self.delegate?.userLoggedIn(data: userEmail! )
UserDefaults.set(UserDefaults.standard)(true, forKey: "isUserLoggedIn");
let mainPage = self.storyboard?.instantiateViewController(withIdentifier: "ViewController")
let mainPageNav = UINavigationController(rootViewController: mainPage!)
let appDelegate = UIApplication.shared.delegate
appDelegate?.window??.rootViewController = mainPageNav
self.dismiss(animated: true, completion: nil)
}
AppDelegate:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let isUserLoggedIn = UserDefaults.bool(UserDefaults.standard)(forKey: "isUserLoggedIn")
if(!isUserLoggedIn) {
let loginViewController = mainStoryboard.instantiateViewController(withIdentifier: "LoginViewController")
self.window?.rootViewController = loginViewController
window!.makeKeyAndVisible()
} else {
let protectedPage = mainStoryboard.instantiateViewController(withIdentifier: "ViewController")
self.window?.rootViewController = protectedPage
window!.makeKeyAndVisible()
}
return true
}
The issue seems to lie in your Login view controller. You're passing the userEmail to your first instance of ViewController, then creating a new instance of ViewController and setting it as the root, which releases the first one. The first step should be to remove this code:
let mainPage = self.storyboard?.instantiateViewController(withIdentifier: "ViewController")
let mainPageNav = UINavigationController(rootViewController: mainPage!)
let appDelegate = UIApplication.shared.delegate
appDelegate?.window??.rootViewController = mainPageNav
In your AppDelegate, you should set the root view controller to ViewController, and modally present the LoginViewController on top of it if the user is not logged in. Make sure to set the LoginViewController's delegate to ViewController. Then your login logic can be:
if(returnValue != "error") {
self.delegate?.userLoggedIn(data: userEmail!)
UserDefaults.standard.set(true, forKey: "isUserLoggedIn")
self.dismiss(animated: true, completion: nil)
}
I make walkthrough (onboarding flow) in my app and I'd like to have a skip button.
The button is located on viewController, so I figured out that the best way to move to another viewController would be access app delegate window.
However, it keeps getting me an error that AppDelegate.Type does not have a member called "window".
#IBAction func skipWalkthrough(sender: AnyObject) {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
AppDelegate.window!.rootViewController = RootViewController
}
Is there anything wrong with such approach?
Thanks in advance!
You have a typo it is supposed to be appDelegate not AppDelegate. So like this:
#IBAction func skipWalkthrough(sender: AnyObject) {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window!.rootViewController = RootViewController
}
Swift 3.2
#IBAction func skipWalkthrough(_ sender: AnyObject) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window!.rootViewController = controller
}
This is for with or without Storyboard and it is working for Swift 3+
let appDelegate = UIApplication.shared.delegate as? AppDelegate
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let homeController = mainStoryboard.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
appDelegate?.window?.rootViewController = homeController
Swift 3
This is a better way:
if let window = NSApplication.shared().windows.first {
window.acceptsMouseMovedEvents = true;
}
appDelegate.window!.rootViewController is not working in Swift 5
Here is working code
Add below extension
extension UIWindow {
static var key: UIWindow! {
if #available(iOS 13, *) {
return UIApplication.shared.windows.first { $0.isKeyWindow }
} else {
return UIApplication.shared.keyWindow
}
}
}
use
let mainSB = UIStoryboard(name: "Main", bundle: nil)
if let RootVc = mainSB.instantiateViewController(withIdentifier: "NavigationController") as? UINavigationController{
UIWindow.key.rootViewController = RootVc
}
UIWindow.key // to access only window
You can also use conditional binding to reach the window.
if let window = UIApplication.shared.windows.first {
// use window here.
}
You are using the protocol name (i.e. AppDelegate) instead of the instance:
Should be:
appDelegate.window!.rootViewController = RootViewController
You can access tab bar anywhere from the app. Use below:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
if let tabBarController = appDelegate.window!.rootViewController as? UITabBarController {
if let tabItems = tabBarController.tabBar.items {
let tabItem = tabItems[2]
tabItem.badgeValue = "5" //enter any value
}
}
This solution work for :
After Login / Register Programmatically add UITabbarController
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window!.rootViewController = tabs
appDelegate.window!.makeKeyAndVisible()