Swift 3 - Close current ViewController and Open new ViewController - ios

I've gone through a tad amount of SO posts about this but none of them clearly state how to get this done in Swift 3.
Simply put, I need to close the current view that is displayed and open a new view controller. Such that the only view controller open would be the 2nd VC
This is the code I tried using, but when I try this the view controller gets closed without the rest of the code getting executed
//Close current VC
self.dismiss(animated: true, completion: nil)
//Open the new VC
let mainStoryboard: UIStoryboard = UIStoryboard(name:"Main",bundle:Bundle.main)
let secondViewController: SecondViewController = mainStoryboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
self.present(secondViewController, animated: true, completion: nil)
Can someone help me out? I just need a simple transition from 1st VC to 2nd VC and close the 1st VC.

The problem is, that "self" can not present the new ViewController since it is the ViewController that is getting dismissed.
You could try calling self.presentingViewController?.present(secondViewController, animated: true, completion: nil)

After spend full day i got a solution on Apple' developer website (on this link)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondVC = storyboard.instantiateViewController(identifier: "second_view_controller")
secondVC.modalPresentationStyle = .fullScreen
secondVC.modalTransitionStyle = .crossDissolve
present(secondVC, animated: true, completion: nil)

If you presented that view controller from some view controller or it is the viewcontroller from navigation or tab viewcontroller, then try to get last visible view controller, code is in this link
Get the current displaying UIViewController on the screen in AppDelegate.m
or else use this code (I have copied from that link)
public extension UIWindow {
public var visibleViewController: UIViewController? {
return UIWindow.getVisibleViewControllerFrom(self.rootViewController)
}
public static func getVisibleViewControllerFrom(_ vc: UIViewController?) -> UIViewController? {
if let nc = vc as? UINavigationController {
return UIWindow.getVisibleViewControllerFrom(nc.visibleViewController)
} else if let tc = vc as? UITabBarController {
return UIWindow.getVisibleViewControllerFrom(tc.selectedViewController)
} else {
if let pvc = vc?.presentedViewController {
return UIWindow.getVisibleViewControllerFrom(pvc)
} else {
return vc
}
}
}
}
Then present your viewcontroller from that visibleViewController -
//Close current VC
self.dismiss(animated: true, completion: nil)
//Open the new VC
let mainStoryboard: UIStoryboard = UIStoryboard(name:"Main",bundle:Bundle.main)
let secondViewController: SecondViewController = mainStoryboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
window?.visibleViewController?.present(secondViewController, animated: true, completion: nil)
If your view controller is the root view controller, then set it to root view controller
if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.window?.rootViewController = secondViewController
}

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 show Form Sheet from AppDelegate?

The AppDelegate checks whether the user is logged in or not, and if not, shows the formSheet view from wherever the user is in the application.
How do I do this?
You can use the window property like rootViewController in appdelegate class
Try this:-
if let rootVC = self.window?.rootViewController as? YourViewController {
//Add the code here to present signupviewcontroller as form sheet
}
if your rootVC is UINavigationController than use visibleViewController property of UINavigationController
if let nsvigationVC = self.window?.rootViewController as? UINavigationController {
if yourVC = nsvigationVC.visibleViewController {
//Add the code here to present signupviewcontroller as form sheet
let sb = UIStoryboard(name: “Main”, bundle: nil) //get signupviewcontroller from storyboard
let signupVC = sb.instantiateViewControllerWithIdentifier("signupviewcontroller") as! signupviewcontroller
signupVC.modalPresentationStyle = UIModalPresentationStyle.FormSheet
signupVC.preferredContentSize = CGSize(width: 365, height: 500) // set content size as per your requirement
yourVC.presentViewController(signupVC, animated: true, completion: nil)
}
}

How to presentViewController embedded in UITabBarController and UINavigationBarController

When using 3D Touch Shortcuts from the home screen I am trying to segue to different view controller.
The application is embedded within a UITabBarController and each tab root controller is a UINavigationController.
Here is how I attempted handling the shortcuts to load the view controller for each shortcut.
private func handleShortcutItem(shortcutItem: UIApplicationShortcutItem) {
if let rootViewController = window?.rootViewController, let shortcutItemType = ShortcutItemType(shortcutItem: shortcutItem) {
let sb = UIStoryboard(name: "main", bundle: nil)
let helloVC = sb.instantiateViewControllerWithIdentifier("HelloVC") as! HelloViewController
let goodbyeVC = sb.instantiateViewControllerWithIdentifier("GoodbyeVC") as! GoodbyeViewController
switch shortcutItemType {
case .Hello:
rootViewController.presentViewController(helloVC, animated: true, completion: nil)
break
case .Goodbye:
rootViewController.presentViewController(goodbyeVC, animated: true, completion: nil)
break
}
}
}
With this code the shortcuts only open the application to the initial view controller and not to the helloVC and goodbyeVC controllers which are in different tabs.
I am presuming this is because the ViewControllers I am trying to load are embedded within a UINavigationController as well as embedded within the UITabBarController.
How can I presentViewController which is embedded within the UITabBarController and UINavigationController ?
UPDATE
I am unsure if the following works as I have not got a iPhone 6S with me atm. But I have changed the code to the following, hopefully this will load the selected tab index when the 3D Touch Action is performed. From there it should post a notification to the view controller to perform a segue.
private func handleShortcutItem(shortcutItem: UIApplicationShortcutItem) {
if let rootViewController = window?.rootViewController, let shortcutItemType = ShortcutItemType(shortcutItem: shortcutItem) {
let tababarController = rootViewController as! UITabBarController
switch shortcutItemType {
case .Hello:
tababarController.selectedIndex = 1
NSNotificationCenter.defaultCenter().postNotificationName("performsegueHello", object: nil)
break
case .Goodbye:
tababarController.selectedIndex = 4
NSNotificationCenter.defaultCenter().postNotificationName("performsegueGoodbye", object: nil)
break
}
}
}
Please try the following code. I think you are not reaching the navigation controller of tab bar.you can do this by :
let insideNvc = tvc?.selectedViewController as? UINavigationController
Now, here is your navigation controller you can present or push anything on it.
private func handleShortcutItem(shortcutItem: UIApplicationShortcutItem) {
let nvc = self.window?.rootViewController as? UINavigationController
let tvc = nvc?.topViewController as? TabBarController
let insideNvc = tvc?.selectedViewController as? UINavigationController
if let shortcutItemType = ShortcutItemType(shortcutItem: shortcutItem) {
let sb = UIStoryboard(name: "main", bundle: nil)
let helloVC = sb.instantiateViewControllerWithIdentifier("HelloVC") as! HelloViewController
let goodbyeVC = sb.instantiateViewControllerWithIdentifier("GoodbyeVC") as! GoodbyeViewController
switch shortcutItemType {
case .Hello:
insideNvc?.presentViewController(helloVC, animated: true, completion: nil)
break
case .Goodbye:
insideNvc?.presentViewController(goodbyeVC, animated: true, completion: nil)
break
}
}
}

using presentviewcontroller inside a function swift

I am trying to use this inside my function to present a view controller after a function runs, so included it inside
let window = UIWindow()
let rootViewController = window.rootViewController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let setViewController = mainStoryboard.instantiateViewControllerWithIdentifier("EmployeeTBCIdentifier")
rootViewController?.presentViewController(setViewController, animated: true, completion: nil)
It's not doing anything, it works if I use it inside viewDidLoad()
here's the main idea, and would it be possible to use completions according to this example,
func logInType(completion(Void1, Void2)->()) {
if login == employer{
void1
}else if login == employee{
void2
}
}
when using the function I want to return this
func logInType{(void1, void2) -> void in
void1 = self.presentview //I want to push view to another if this code executed
void2 = self.presentview //same idea
}
updated : code is working but it's not pushing to the viewcontroller identified.
dispatch_async(dispatch_get_main_queue()){
let window = UIWindow()
let rootViewController = window.rootViewController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let setViewController = mainStoryboard.instantiateViewControllerWithIdentifier("EmployeeTBCIdentifier")
rootViewController?.presentViewController(setViewController, animated: true, completion: nil)
}
Seems to me that there might be a problem in how you are getting the rootViewController. I think the let window = UIWindow() call is going to return a new UIWindow object instead of what you want which is the window that the root view controller (and your app) is associated with. Continuing this, rootViewController will be nil and your optional chain call rootViewController?.presentViewController(setViewController, animated: true, completion: nil) won't do anything. Use an explicit unwrap via rootViewController!.presentViewController(setViewController, animated: true, completion: nil) and you should see the error
To rectify, get the correct UIWindow another way, either from the UIAppDelegate or from another view controller using OtherViewController.view!.window!
Try using it like so:
dispatch_async(dispatch_get_main_queue())
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("mainView") as! MainViewController
self.presentViewController(vc, animated: true, completion: nil)
}

Navigation and tab bar missing when presenting view controller

I am implementing home screen shortcuts using 3D Touch, and it's working well, however the way I currently have it means that when the shortcut takes the user to a specific view controller, the tab bar and navigation bar is missing.
This is my code:
func handleShortCutItem(shortcutItem: UIApplicationShortcutItem) -> Bool {
var handled = false
if let shortcutType = ShortcutType.init(rawValue: shortcutItem.type) {
let rootViewController = window!.rootViewController
switch shortcutType {
case .Favourites:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootController = storyboard.instantiateViewControllerWithIdentifier("favourites") as! FavouritesTableViewController
rootController.parkPassed = DataManager.sharedInstance.getParkByName(NSUserDefaults.standardUserDefaults().stringForKey("currentPark")!)
self.window?.rootViewController = rootController
self.window?.makeKeyAndVisible()
handled = true
}
return handled
}
Can anyone suggest what I need to change in the code?
This is the starboard layout (FavouritesTableViewController is indicated):
EDIT:
Here is my updated code:
#available(iOS 9.0, *)
func handleShortCutItem(shortcutItem: UIApplicationShortcutItem) -> Bool {
var handled = false
if let shortcutType = ShortcutType.init(rawValue: shortcutItem.type) {
switch shortcutType {
case .Favourites:
print("favourites")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootController = storyboard.instantiateViewControllerWithIdentifier("favourites") as! FavouritesViewController
rootController.parkPassed = DataManager.sharedInstance.getParkByName(NSUserDefaults.standardUserDefaults().stringForKey("currentPark")!)
let root = UIApplication.sharedApplication().delegate as! AppDelegate
if let navCont = root.window?.rootViewController?.navigationController {
navCont.presentViewController(rootController, animated: true, completion: nil)
} else {
root.window?.rootViewController?.presentViewController(rootController, animated: true, completion: nil)
}
root.window?.makeKeyAndVisible()
handled = true
}
}
return handled
}
Try this:
Get the delegate from app delegate:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
GEt the controller you would like to to present from storyboard:
Controller *cont=//get the reference from storyboard either using storyboard ID
and then make your rootview present the other controller:
[appDelegate.window.rootViewController presentViewController:cont animated:YES completion:^{
DLog(#"presented your view ON TOP of the tab bar controller");
}];
Swift:
var appDelegate: AppDelegate = UIApplication.sharedApplication().delegate()
var cont: Controller = //get the reference form storyboard
appDelegate.window.rootViewController.presentViewController(cont, animated: true, completion: { DLog("presented your view ON TOP of the tab bar controller")
})
you can move the presenting stuff on main thread,if you like!!!!
So I got the solution I hope that will work for you.
First you have to set your Storyboard instance.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
after that you need to set where you want to start the navigation.
let mynVC = storyboard.instantiateViewControllerWithIdentifier("root") as! UINavigationController
right now you can set the viewcontroller that you want to appear
let playVC = storyboard.instantiateViewControllerWithIdentifier("playVC")
So and now you can start the workflow but note that you have to do this in a completion like this
self.window?.rootViewController?.presentViewController(rootVC, animated: true, completion: { () -> Void in
rootVC.pushViewController(playVC, animated: true)
})
So your rootViewController will present you "rootVC" and after that your playVC.
I hope It will help you guys:)
The problem is, you are setting the view controller(named:rootController) to be the window?.rootViewController, other than presenting the rootController by the window?.rootViewController. Just change
self.window?.rootViewController = rootController
to
self.window?.rootViewController.presentViewController(rootController, animated: true, completion: nil)
if you use it like this you will be replaced rootview to this viewcontroller. So this will be a new solo page and its normal to dont have navigationController or tabbarController. Because you have only this new page in view hierarchy.
If you want to present this from rootview, you may try
let root=UIApplication.sharedApplication().delegate as! AppDelegate
if let navCont=root.window?.rootViewController?.navigationController
{
navCont.presentViewController(viewControllerToPresent: UIViewController, animated: true, completion: nil)
}
else{
root.window?.rootViewController?.presentViewController(viewControllerToPresent: UIViewController, animated: true, completion: nil)
}

Resources