I have one view controller called login VC with navigation view controller.And i connect two button in my LoginVc.First button will navigate to signUp Screen as push segue.Second button will navigate to forgot password screen as push segue.
Now when user logged in and enter in to my app means.There is one button name called Log out.I have written the code functionality like, when user press log out button one uialert pop up will ask "Do You want to log out".If user press they will navigate to again login screen .
So after user pressed log out and if user came to login screen and if user press my two button [ i. e means forgot password screen, sign up screen button].Then i am getting crash.
Crash report :
Terminating app due to uncaught exception 'NSGenericException', reason: 'Push segues can only be used when the source controller is managed by an instance of UINavigationController.'
My log out button code :
#IBAction func LogoutButton(sender: AnyObject) {
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "ISLOGGEDIN")
NSUserDefaults.standardUserDefaults().synchronize()
dispatch_async(dispatch_get_main_queue(), {
let alert = UIAlertController(title: "Logout Successful", message: "You have successfully logged out.", preferredStyle: UIAlertControllerStyle.Alert)
// add an action (button)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default){action -> Void in
let loginViewController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginViewController") as? LoginViewController
let appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = loginViewController
})
// show the alert
self.presentViewController(alert, animated: true, completion: nil)
})
}
I try adding this line :
let vc: UINavigationController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController") as! UINavigationController
But i am getting same problem.Now how can i re write my code in my log out button action method.To redirect to login screen with navigation bar.
Please help me out.Thanks!
You are setting the RootViewController to a ViewController but your application is NavigationViewController. Your RootViewController should be a NavigationController. So add your LoginViewController to NavigationViewController and set that rootViewController of window.
let loginViewController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginViewController") as? LoginViewController
let navigationController = UINavigationController(rootViewController: vc)
window.rootViewController = navigationController
why don't you do like this:
self.navigationController?.pushViewController(loginViewController!, animated: true)
and when you want to come back:
self.navigationController?.popViewControllerAnimated(true)
According to your error there is no navigation controller found in your view hierarchy.
Select your loginViewController from story board and from Editor embed in Navigation controller. and use performSegue to push viewcontroller and use popViewController or popTorootviewcontroller to go back in navigation controller.
hope this will help :)
Click to viewController in storyBoard
Go to Editor -> Embed In -> Navigation Controller this will add navigation on this view controller.
Then you can push and pop from one view controller to another view controller.
Ex : Push
var dash : XyzViewController = storyBoard.instantiateViewControllerWithIdentifier("xyz") as! XyzViewController
self.navigationController?.pushViewController(dash, animated: true)
Pop:
self.navigationController?.popViewControllerAnimated(true)
Try this solution:
let vc = self.storyboard?.instantiateViewController(withIdentifier: "yourVC") as! yourViewController
let navigationController = UINavigationController(rootViewController: vc)
self.present(navigationController, animated: true, completion: nil)
Related
I have 3 ViewController : LoginViewController, CheckinViewController, & ProfileViewController
The flow is :
LoginVC --> CheckinVC --> ProfileVC
What i need is:
I want to dismiss "ProfileVC" & "CheckinVC" when click logout button in "ProfileVC" then go back to the "LoginVC"
LoginVC.swift
let checkinViewController = self.storyboard?.instantiateViewController(withIdentifier: "CheckinViewController") as! CheckinViewController
self.navigationController?.pushViewController(checkinViewController, animated: true)
JustHUD.shared.hide()
self.dismiss(animated: false, completion: nil)
CheckinVC.swift
if let profileView = self.storyboard?.instantiateViewController(withIdentifier: "ProfileViewController") {
profileView.providesPresentationContextTransitionStyle = true
profileView.definesPresentationContext = true
profileView.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext;
// profileView.view.backgroundColor = UIColor.init(white: 0.4, alpha: 0.8)
profileView.view.backgroundColor = UIColor.clear
profileView.view.isOpaque = false
self.present(profileView, animated: true, completion: nil)
Here is i'm trying to do
ProfileVC.swift
#IBAction func clickLogout(_ sender: Any) {
UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)
UserDefaults.standard.synchronize()
self.dismiss(animated: false, completion: {
print("ProfileView : dismiss completed")
let loginViewController = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
self.navigationController?.pushViewController(loginViewController, animated: true)
self.dismiss(animated: false, completion: {
print("SUCCESS")
})
})
}
Well you need to do an Unwind Segue so you can go back in your LoginVC.
Follow these simple four steps to create Unwind segues:
In the view controller you are trying to go back to, LoginVC in your example, write this code:
#IBAction func unwindToVC1(segue:UIStoryboardSegue) { }
( Remember: It’s important to insert this method in the view
controller you are trying to go back TO! )
In the Storyboard, go to the screen you’re trying to unwind from ( ProfileVC in our case ), and control + drag the viewController icon to the Exit icon located on top.
3. Go to the document outline of the selected viewController in the Storyboard, select the unwind segue as shown below.
Now, go to the Attributes Inspector in the Utilities Pane and name the identifier of the unwind segue.
Finally, write this code where you want the unwind segue action to be triggered, ProfileVC in our case.
#IBAction func clickLogout(_ sender: Any) {
UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)
UserDefaults.standard.synchronize()
performSegue(withIdentifier: "unwindSegueToVC1", sender: self)
}
For more information check Create Unwind Segues
While Enea Dume's solution is correct if you want to use storyboards, here is the explanation of the problem, and the solution if you want to do it in code like you have been so far.
The Issue
If we focus on the self.dismiss calls in the logoutFunction in ProfileVC, this is what happens.
The first time you call self.dismiss, ProfileVC will dismiss itself and be removed from the view stack.
In the completion delegate, you are pushing a new LoginVC to a navigation controller. However, the CheckIN VC is presented over the navigation controller so you can't see anything happen.
The second call to self.dismiss does nothing as ProfileVC is not presenting any other view controllers and it is not in the stack any longer.
The Solution
You need to keep a reference to LoginVC that presented the CheckInVC. If you call "reference to LoginVC".dismiss, it will dismiss the view controllers above it in the stack and take you back to the login view controller.
In the class CheckinVC.swift, in the function viewDidAppear, check if the user is still active or not based on the session that you are maintaining and the pop to login view controller accordingly. If user status is logged out, then it will go to login view controller. Else it will work as usual.
Problem is that you are trying to dismiss twice ProfileVC but what you actually need is to dismiss it and then pop CheckinVC.
Also after dismissing a view controller it no longer has a reference to the NavigationController so you need a reference to it in a temporal variable.
Change ProfileVC like this:
#IBAction func clickLogout(_ sender: Any) {
UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)
UserDefaults.standard.synchronize()
let navigationController = self.navigationController
let loginViewController = storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
self.dismiss(animated: false, completion: {
print("ProfileView : dismiss completed")
navigationController?.pushViewController(loginViewController, animated: true)
navigationController?.popViewController(animated: false)
})
}
I have app with 7 screens(VC) which are embedded in navigation controller.
And I have one separate VC that is not embedded. This VC is acting like custom popup class PopupView: UIViewController {} and get's called by pressing button from every screen that I have in my app using custom segue (setup as custom segue in storyboard):
open class MIBlurPopupSegue: UIStoryboardSegue {
open override func perform() {
MIBlurPopup.show(destination, on: source)
} }
In this popup there's a button that should open another VC (VC always the same) that is embedded in navigation stack.
What I'm trying to achieve is to actually open that VC that is inside navigation stack by pressing button in Popup VC and then get back to the screen where Popup was called.
So, user journey will look like - Opened VC1(2,3,5,6,7) -> Called popup VC -> Pressed button -> opened VC4 -> pressed navigation back button -> returned to VC1.
I what I have right now:
Connected in storyboard all 6 screens to VC4, with segues ids
Tried performSegue(withIdentifier: "toVC4"), present(vc, animated: true, completion: nil)
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "VC4")
self.present(controller, animated: true, completion: nil)
Using protocols to call func in VC1 but failed.
I'm definitely missing something and would be very thankful if someone could provide code sample to solve this issue.
You can try this inside the popup button's action
self.dismiss(animated:true) {
if let nav = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController {
let vc1 = storyboard!.instantiateViewController(withIdentifier: "MenuId")
let finalVC = storyboard!.instantiateViewController(withIdentifier: "finalId")
nav.setViewControllers([vc1,vc4],animated:true) // set it to false if you want
}
}
I want to send a user to a specific ViewController in my app once a notification is clicked.
I now that I can do something like this:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationViewController = storyboard.instantiateViewControllerWithIdentifier("Home") as? HomeViewController
presentedVC?.presentViewController(destinationViewController!, animated: true, completion: nil)
But my app has a tab bar and looks like this
Tab bar
tab1: navigationController -> VC1
tab2: navigationController -> VC2 -> HomeVC
tab: navigationController -> VC3
Each tab has a navigationController as a infront of it.
So how can I send the user to HomeVC? I must first select tab 2 then the navigation controller then push the user tvice:
tab2: navigationController -> VC2 -> HomeVC
And the other problem, if there any way to tell if the user is already in HomeVC? I dont want to send the user to the same VC if his already there.
You must have access to your UITabbarController in you UIApplicationDelegate or wherever you're handling the notification tap.
let tabBar:UITabBarController = self.window?.rootViewController as! UITabBarController //or whatever your way of getting reference is
So first you'll get the reference to UINavigationController in your second tab like this:
let navInTab:UINavigationController = tabBar.viewControllers?[1] as! UINavigationController
Now push your home view at second tab's navigation controller:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationViewController = storyboard.instantiateViewControllerWithIdentifier("Home") as? HomeViewController
navInTab.pushViewController(destinationViewController!, animated: true)
And finally switch your tab to second to show the just pushed home controller
tabBar.selectedIndex = 1
Keep in mind, this answer assumes that your application has already set the tab bar as the root view controller of application window prior handling the notification tap.
Try something like this:
if let tabBarController = window?.rootViewController as? UITabBarController {
tabBarController.selectedIndex = 1 // in your case the second tab
}
The idea is to switch to get the tab bar instance and switch it to your desired tab (where you have your view controller).
The above code works in AppDelegate / you can easily call it anywhere by getting the tabBarController instance.
You can check which tab is selected by user with the method var selectedIndex: Int. You can check which view controller is present like this self.navigationController?.presentingViewController?.presentedViewController. This will solve your problem.
this question may asked before but i can't find any related question thats why posting a new one
so i have 3 tabs and in tab3 i have user profile Tab
in tab3 i wanna show loginViewController (if user haven't logged in yet) else i want to show the default profile tab
i can pass a segue using performSegue or present a view controller like this :
let loginPageView = self.storyboard?.instantiateViewController(withIdentifier: "LoginVC") as! SignupViewController
self.present(loginPageView, animated: true, completion: nil)
but i dont want that ,
i wanna show a different VC at the place of Default VC if user haven't logged in yet without any animation or transition
e.g.
producthunt's iOS app
if user's didn't logged in
and if he did logged in:
see the first image they are not presenting it as pop over or modal.
it seems like they do it on a same VIewController but i'm not sure. anybody can guide me here ?
replacing current Viewcontroller with desired ViewController did the job
let loginPageView = self.storyboard?.instantiateViewController(withIdentifier: "LoginVC") as! SignupViewController
var VCs = self.navigationController?.viewControllers
VCs?.removeLast() // here i removed the current VC
VCs?.append(loginPageView) after that added the desired VC
self.navigationController?.setViewControllers(VCs!, animated: false) // here i'm setting the viewcontrollers with any animation.
This is my storyboard:
Short Description:
My App starts with the LaunchController
a modal segue shows the Reveal View Controller
this bring the Menu Controller and my Main Navigation Controller (ID
"NavController"; green Navbar) together. this will create a slide
menu. (Basic Code:
appcoda.com/ios-programming-sidebar-navigation-menu/)
my Main Navigation Controller shows a TableViewController.
this one have a menu button (3 Lines) which make the slide menu
visible
the plus icon willo push the last view Controller (ID: "VC1").
My Problem:
I would like to set quick actions for my app.
This code help works for that:
#available(iOS 9.0, *)
func handleShortcut( shortcutItem:UIApplicationShortcutItem ) -> Bool {
var succeeded = false
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewControllerWithIdentifier("NavController")
self.window?.rootViewController = initialViewController
let navVC = self.window?.rootViewController as! UINavigationController
switch shortcutItem.type {
default:
navVC.pushViewController(storyboard.instantiateViewControllerWithIdentifier("VC1"), animated: false)
succeeded = true
break
}
return succeeded
}
This Code set the NavController as initial Controller und push the to VC1.
This works fine.
With the X icon in VC 1, i use the unwind function back to TableView.
The Problem: if i use the undwind function and fall back to tableview i can't open the slide menu. A Touch on the menu icon give no reaction.
The Problem can be, that i start the app with the quick action behind the Reveal View Controller.
How can I solve this problem?
Did you try to make the reveal view controller the entry point of your app and deleting the launch controller, because if you just want to go back to the main view you could just do this :
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateInitialViewController() as UIViewController!
self.presentViewController(controller, animated: false, completion: nil)
If you cannot could you explain the purpose of the launch controller?