using presentviewcontroller inside a function swift - ios

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

Related

EXC_BAD_ACCESS (code=2, address=0x16a60ffe0) [duplicate]

Issue
I started taking a look on the Swift Programming Language, and somehow I am not able to correctly type the initialization of a UIViewController from a specific UIStoryboard.
In Objective-C I simply write:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"StoryboardName" bundle:nil];
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"ViewControllerID"];
[self presentViewController:viewController animated:YES completion:nil];
Can anyone help me on how to achieve this on Swift?
This answer was last revised for Swift 5.4 and iOS 14.5 SDK.
It's all a matter of new syntax and slightly revised APIs. The underlying functionality of UIKit hasn't changed. This is true for a vast majority of iOS SDK frameworks.
let storyboard = UIStoryboard(name: "myStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "myVCID")
self.present(vc, animated: true)
Make sure to set myVCID inside the storyboard, under "Storyboard ID."
For people using #akashivskyy's answer to instantiate UIViewController and are having the exception:
fatal error: use of unimplemented initializer 'init(coder:)' for class
Quick tip:
Manually implement required init?(coder aDecoder: NSCoder) at your destination UIViewController that you are trying to instantiate
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
If you need more description please refer to my answer here
This link has both the implementations:
Swift:
let viewController:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController") as UIViewController
self.presentViewController(viewController, animated: false, completion: nil)
Objective C
UIViewController *viewController = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:#"ViewController"];
This link has code for initiating viewcontroller in the same storyboard
/*
Helper to Switch the View based on StoryBoard
#param StoryBoard ID as String
*/
func switchToViewController(identifier: String) {
let viewController = self.storyboard?.instantiateViewControllerWithIdentifier(identifier) as! UIViewController
self.navigationController?.setViewControllers([viewController], animated: false)
}
akashivskyy's answer works just fine! But, in case you have some trouble returning from the presented view controller, this alternative can be helpful. It worked for me!
Swift:
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController") as! UIViewController
// Alternative way to present the new view controller
self.navigationController?.showViewController(vc, sender: nil)
Obj-C:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MyStoryboardName" bundle:nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"someViewController"];
[self.navigationController showViewController:vc sender:nil];
Swift 4.2 updated code is
let storyboard = UIStoryboard(name: "StoryboardNameHere", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "ViewControllerNameHere")
self.present(controller, animated: true, completion: nil)
// "Main" is name of .storybord file "
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
// "MiniGameView" is the ID given to the ViewController in the interfacebuilder
// MiniGameViewController is the CLASS name of the ViewController.swift file acosiated to the ViewController
var setViewController = mainStoryboard.instantiateViewControllerWithIdentifier("MiniGameView") as MiniGameViewController
var rootViewController = self.window!.rootViewController
rootViewController?.presentViewController(setViewController, animated: false, completion: nil)
This worked fine for me when i put it in AppDelegate
If you want to present it modally, you should have something like bellow:
let vc = self.storyboard!.instantiateViewControllerWithIdentifier("YourViewControllerID")
self.showDetailViewController(vc as! YourViewControllerClassName, sender: self)
I would like to suggest a much cleaner way. This will be useful when we have multiple storyboards
1.Create a structure with all your storyboards
struct Storyboard {
static let main = "Main"
static let login = "login"
static let profile = "profile"
static let home = "home"
}
2. Create a UIStoryboard extension like this
extension UIStoryboard {
#nonobjc class var main: UIStoryboard {
return UIStoryboard(name: Storyboard.main, bundle: nil)
}
#nonobjc class var journey: UIStoryboard {
return UIStoryboard(name: Storyboard.login, bundle: nil)
}
#nonobjc class var quiz: UIStoryboard {
return UIStoryboard(name: Storyboard.profile, bundle: nil)
}
#nonobjc class var home: UIStoryboard {
return UIStoryboard(name: Storyboard.home, bundle: nil)
}
}
Give the storyboard identifier as the class name, and use the below code to instantiate
let loginVc = UIStoryboard.login.instantiateViewController(withIdentifier: "\(LoginViewController.self)") as! LoginViewController
No matter what I tried, it just wouldn't work for me - no errors, but no new view controller on my screen either. Don't know why, but wrapping it in timeout function finally made it work:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.0) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "TabletViewController")
self.present(controller, animated: true, completion: nil)
}
Swift 3
let settingStoryboard : UIStoryboard = UIStoryboard(name: "SettingViewController", bundle: nil)
let settingVC = settingStoryboard.instantiateViewController(withIdentifier: "SettingViewController") as! SettingViewController
self.present(settingVC, animated: true, completion: {
})
Swift 4:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let yourVC: YourVC = storyboard.instantiateViewController(withIdentifier: "YourVC") as! YourVC
If you have a Viewcontroller not using any storyboard/Xib, you can push to this particular VC like below call :
let vcInstance : UIViewController = yourViewController()
self.present(vcInstance, animated: true, completion: nil)
I know it's an old thread, but I think the current solution (using hardcoded string identifier for given view controller) is very prone to errors.
I've created a build time script (which you can access here), which will create a compiler safe way for accessing and instantiating view controllers from all storyboard within the given project.
For example, view controller named vc1 in Main.storyboard will be instantiated like so:
let vc: UIViewController = R.storyboard.Main.vc1^ // where the '^' character initialize the controller
Swift 5
let vc = self.storyboard!.instantiateViewController(withIdentifier: "CVIdentifier")
self.present(vc, animated: true, completion: nil)
I created a library that will handle this much more easier with better syntax:
https://github.com/Jasperav/Storyboardable
Just change Storyboard.swift and let the ViewControllers conform to Storyboardable.
guard let vc = storyboard?.instantiateViewController(withIdentifier: "add") else { return }
vc.modalPresentationStyle = .fullScreen
present(vc, animated: true, completion: nil)
if let destinationVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DestinationVC") as? DestinationVC{
let nav = self.navigationController
//presenting
nav?.present(destinationVC, animated: true, completion: {
})
//push
nav?.pushViewController(destinationVC, animated: true)
}
I use this helper:
struct Storyboard<T: UIViewController> {
static var storyboardName: String {
return String(describing: T.self)
}
static var viewController: T {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let vc = storyboard.instantiateViewController(withIdentifier: Self.storyboardName) as? T else {
fatalError("Could not get controller from Storyboard: \(Self.storyboardName)")
}
return vc
}
}
Usage (Storyboard ID must match the UIViewController class name)
let myVC = Storyboard.viewController as MyViewController

Push view controller not working in Swift

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let playGameViewController = (storyboard.instantiateViewController(withIdentifier: "PlayGameViewController") as! PlayGameViewController)
self.navigationController?.pushViewController(playGameViewController, animated: true)
push not worked but I tried present working, I want navigate with push.
Try running the below code to know where you have gone wrong.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let playGameViewController = storyboard.instantiateViewController(withIdentifier: "PlayGameViewController") as? PlayGameViewController else {
print("This means you haven't set your view controller identifier properly.")
return
}
guard let navigationController = navigationController else {
print("This means you current view controller doesn't have a navigation controller")
return
}
navigationController.pushViewController(playGameViewController, animated: true)
Try using breakpoints to figure out if any variable is nil. In your case, there is more probability for having your navigationController being nil.

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
}

Swift 3 - Close current ViewController and Open new ViewController

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
}

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