Pass object to viewcontroller in Swift - ios

In Objective-C if you need to pass an object to a ViewController you can do:
in destination .h file
#property NSObject * contact;
in sending .m file
MyVC *destVC = [self.storyboard instantiateViewControllerWithIdentifier:#"myVC"];
destVC.contact = contact;
[self.navigationController pushViewController:destVC animated:YES];
I am trying to do this in Swift with this code:
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let destVC = storyBoard.instantiateViewController(withIdentifier: "contactDetail")
//Following line causes error
//destVC.contact = latestContact
self.navigationController!.pushViewController(destVC, animated: true)
The VC launches ok but when I try to pass it a variable with destVC.contact = latestContact then compiler says
value of type ViewController has no such member contact.

Cast it to your subclassed view controller. Say your contact details view controller class name is ContactDetailsViewController
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let destVC = storyBoard.instantiateViewController(withIdentifier: "contactDetail") as? ContactDetailsViewController {
destVC.contact = latestContact
self.navigationController!.pushViewController(destVC, animated: true)
}

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.

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

Call the same view controller from many other view controller programmatically without using any segue

let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController")
Run code above in multiple view controller they will init multiple someViewController. How to call the same someViewController instance in this case?
I think this is what you need to use for that effect Using a dispatch_once singleton model in Swift
More specific:
Swift :
class SomeViewController {
class var sharedInstance: SomeViewController {
struct Static {
static let instance: SomeViewController = UIStoryboard(name: "MyStoryboardName", bundle: nil).instantiateViewControllerWithIdentifier("someViewController")
}
return Static.instance
}
}
Objective C:
+ (instancetype)sharedInstance; // add this to SomeViewController.h file
+ (instancetype)sharedInstance { // and this in SomeViewController.m
static SomeViewController *instance = nil;
if (!instance) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MyStoryboardName" bundle:nil];
instance = [storyboard instantiateViewControllerWithIdentifier:#"SomeViewController"];
}
return instance;
}
then use with [SomeViewController sharedInstance]
Objective C
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
someViewController *afv=[storyboard instantiateViewControllerWithIdentifier:#"someViewController"];
afv.getdata();
[self.sideMenuViewController setContentViewController:afv animated:YES];
Swift
let mainStoryboard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let someobject: someViewController = (mainStoryboard.instantiateViewControllerWithIdentifier("someViewController") as! someViewController)
someobject.getdata()
self.navigationController?.pushViewController(someobject, animated: true)

change URL of UIWebView in view controller from push notification appdelegate with Swift

var storyboard = UIStoryboard(name: "Main", bundle: nil)
var viewController: UIViewController = storyboard.instantiateViewControllerWithIdentifier("view0000") as UIViewController
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
how to get UIWebView in appdelegate.swift
Create a property for the URL in your viewcontroller and set the value for the property in AppDelegage. Then in your ViewConroller's viewDidLoad method, set the URL property value to your UIWebView.
Like Rshankar said, assuming your viewController has the webView, I would do:
var storyboard = UIStoryboard(name: "Main", bundle: nil)
var viewController: MyViewController = storyboard.instantiateViewControllerWithIdentifier("view0000") as MyViewController
viewController.url = "http://stackoverflow.com"
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
And this would be your MyViewController:
And whatever you want in reloadWebview
class MyViewController:UIViewController {
var url = "" {
didSet {
reloadWebView()
}
}
}

Resources