How to instantiate a view controller programatically, without storyboard - swift

Is there a way to instantiate a view controller programatically without using storyboard identifier. I want to push CNContactPickerController into my root view controller .
let controller = CNContactPickerViewController()
controller.delegate = self
navigationController?.present(controller, animated: true, completion: nil)
i did this but in contactPicker (delegate) i push a VC from storyboard where i save what info i want from that specific contact.
The problem: when i pop the last view controller i want to go on the CNConctactPickerControllerView but i go on the first view controller
i tried with dismiss but nothing happens..

The problem with doing this is that NONE of the UI for your view controller is created. What you are doing is simply instantiating an instance of the class. None of the corresponding UI is created, also your IBOutlets will be nil.
You have 2 options, either you use Interface builder and instantiate from the Nib or Storyboard, or you create ALL your UI manually in code.
For the sake of resuability you could create a static method on CNContactPickerViewController to handle the storyboard instantiation for you. See the following:
class CBContactPickerViewController: UIViewController {
static func fromStoryboard() -> CNContactPickerViewController {
return UIStoryboard(name: "foobar", bundle: nil).instantiateViewController(identifier: "CNContactPickerViewController") as! CNContactPickerViewController
}
}
You can then utilise this as follows:
self.present(viewController: CNContactPickerViewController.fromStoryboard(), animated: true, completion: nil)

Related

How to invoke a method from a modal view controller class in Swift?

Basically for this simple game app I have 2 different UIViewControllers called ViewController and PreviewController. PreviewController is opening view with the title screen and a label titled "Start game". When the label is tapped, it initiates a modal view controller (the ViewController class that has all the views for the actual game itself) and calls the "EnterNewGame" method from ViewController that sets up the game. Right now the issue I have is when calling this method, only part of the method seems to be running.
Here is the function in PreviewController that is being initiated upon tap:
#objc func handleButtonTap(_ recognizer: UITapGestureRecognizer) {
self.present(ViewController(), animated: true, completion: {() -> Void in
ViewController().enterNewGame()
})
}
And here is the EnterNewGame() method from ViewController
func enterNewGame() {
//show suit indicators when starting a new game
bluePlayerSuitsHidden = false
redPlayerSuitsHidden = false
game.blueTurn = true
self.setBackground()
self.cleanUpBoard()
self.createBoard()
self.displayBoard()
self.setSuitIndicators()
self.highlightCards()
playButton.isEnabled = false
}
Right now, when the label is tapped the screen transitions to the modal view controller but only displays a black screen with only one of the game setups (setting a few images on the top of the screen) working properly. I am sure that the EnterNewGame method works properly to actually start the game because I have tested it in isolation, so I think I am just not setting up the modal view controller properly or I have to call the method differently. Any help is appreciated, thanks.
Controller on which you're calling your method ins't the same instance as controller which you're presenting, you need constant (also your code can be simplified by avoiding using self references and writing name of completion parameter with specifing closure's parameter and return type)
#objc func handleButtonTap(_ recognizer: UITapGestureRecognizer) {
let controller = ViewController()
present(controller, animated: true) {
controller.enterNewGame()
}
}
Also, you can call this method on some other method inside your certain controller like viewDidLoad, viewWillAppear or you can create factory method which would return you certain set controller.
This last part leads me to idea: look how you instantiate your controller and look carefully if you don't need to instantiate it through storyboard or nib file.
class ViewController: UIViewController {
class func instantiate() -> ViewController {
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Identifier") as! ViewController
// let controller = ViewController(nibName: "ViewController", bundle: nil)
controller.enterNewGame()
return controller
}
}
Usage:
#objc func handleButtonTap(_ recognizer: UITapGestureRecognizer) {
present(ViewController.instantiate(), animated: true)
}

Casting failure with a view controller instantiated from a storyboard

I have a view controller that instantiates a new window controller.
I need to pass an object to that window controller's view controller.
Here is what I have so far:
let storyboard = NSStoryboard(name: "Main", bundle: nil)
if let windowController = storyboard.instantiateController(withIdentifier: "customerEditWindowController") as? NSWindowController
{
let viewController = windowController.contentViewController as! EditCustomerViewController
viewController.customer = customer // (An object)
windowController.window?.makeKeyAndOrderFront(self)
}
The window is displayed as expected and then Xcode traps into the debugger with the error:
Could not cast value of type 'NSViewController' (0x7fffd1dcc4e0) to
'Inventory2.EditCustomerViewController' (0x100011660).
I'm confused since my EditCustomerViewController extends NSViewController.
class EditCustomerViewController: NSViewController
1) Why can't NSViewController be cast to EditCustomerViewController?
2) Is there a better way to get data into the new view controller?
This is a simple shot in the dark but did you set the view controller’s class name in Interface Builder?

how to segue to storyboard viewcontroller from xib view with swift 3

I'm having the hardest time finding an answer for this.
I have a xib view that is within a scrollview that is within a view controller. In the xib I have a button with an action and I need to segue to a view controller I have in my storyboard. I also would like to be able to use a custom segue.
So far, I have read that I can instantiate the viewcontroller from the storyboard to segue to it. But then I don't know how to present that controller.
thanks for any help...
UPDATE:
this is the code I'm using to perform the segue.
In parent ViewController:
static var referenceVC: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
print("viewdidload")
LevelSelectViewController.referenceVC = self
setupScrollView()
}
code in xib view file
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "sightWordController")
let parent = LevelSelectViewController.referenceVC!
let segue = InFromRightCustomSegue(identifier: "test", source: parent, destination: vc)
segue.perform()
As noted in the comments, Segues are typically confined to storyboard usage as noted in the documentation. You can implement a custom xib view in a storyboard via #IBDesignable like approaches and have you're view load from the xib into the storyboard file/class. This way, you gain the benefits of both worlds. Otherwise, you may want to approach this in another fashion (such as delegates/target-action events, etc).
You may also climb the responder chain and call a segue related to the VC loaded from the storyboard (the segue doesn't necessarily have to be attached to any particular action) via getting a reference to the VC and calling the segue. You can climb the responder chain in a manner such as the example code below:
protocol ChildViewControllerContainer {
var parentViewController: UIViewController? { get }
}
protocol ViewControllerTraversable {
func viewController<T: UIViewController>() -> T?
}
extension UIView: ViewControllerTraversable {
func viewController<T: UIViewController>() -> T? {
var responder = next
while let currentResponder = responder {
guard responder is T else {
responder = currentResponder.next
continue
}
break
}
return responder as? T
}
}
extension UITableViewCell: ChildViewControllerContainer {
weak var parentViewController: UIViewController? {
return viewController() as UIViewController?
}
}

Swift - How to pass UInavigationController and also pass variables

What is the best way to pass a UInavigationController and also pass variables to a new viewController. I know how to do one or the other but not both at the same time. Thank you in advance
this is my current code
func(){
let vc = storyboard?.instantiateViewControllerWithIdentifier("messagesViewController") as! UINavigationController
let posts = self.postList[indexPath.row]
//this is the var that i want to past
//vc.previousViewMessageId = posts.postKey
self.presentViewController(vc, animated: true, completion: nil)
}
If I understand you correctly, you have a view controller that can present a second VC. And this VC is embedded in a UINavigationController. What you don't know how to do, is to pass data from the first VC, to the navigation controller, then to the second VC.
Here is a brute force solution. It's not beautiful, but it works anyway.
Make your own UINavigationController subclass:
class DataPasserController: UINavigationController {
var previousViewMessageId: SomeType?
override func viewDidLoad() {
if let vc = self.topViewController as? YourSecondViewController {
vc.previousViewMessageId = self.previousViewMessageId
}
}
}
Now you can add a navigation controller in the storyboard, set its class to DataPasserController, and connect the second VC to it as its root view controller.
Now suppose you have got an instance of DataPasserController by calling instantiateViewControllerWithIdentifier, you can do this:
yourDataPasserControllerInstance.previousViewMessageId = posts.postKey
And present the instance!
To pass a value to your Navigation Controller's Root View Controller, you access viewControllers[0] and cast it to the class of your Messages View Controller (the controller that has the previousViewMessageId property):
func () {
let messagesNC = storyboard?.instantiateViewControllerWithIdentifier("messagesViewController") as! UINavigationController
let messagesVC = messagesNC.viewControllers.first as! MessagesViewController
messagesVC.previousViewMessageId = postList[indexPath.row].postKey
presentViewController(messagesNC, animated: true, completion: nil)
}
What you have there is simply presenting a view controller... You are skipping the navigation controller.
What you need to do is present the new view controller inside the navigation controller. Once you have done that, it will show correctly. You can also pass the variables after you've created the vc variable.
This presents the new viewController (vc) within the navigation controller...
self.navigationController?.pushViewController(vc, animated: false)
This sets the variable in the new viewController (vc) (you are correct)
vc.previousViewMessageId = posts.postKey
So complete:
func(){
let vc = storyboard?.instantiateViewControllerWithIdentifier("messagesViewController") as! MessagesViewController
let posts = self.postList[indexPath.row]
//this is the var that i want to past
vc.previousViewMessageId = posts.postKey
navigationController?.pushViewController(vc, animated: false)
}
PS. While not part of the question, I feel I should still mention... Use of the word self should be left to necessity only. In other words, don't use it when it isn't needed. for example self.postList[indexPath.row] :)
https://github.com/raywenderlich/swift-style-guide#use-of-self

Change ViewController Swift

i have an issue. I would like to change the view controller in swift.
This is a part of my code:
if success == "1" {
NSLog("Login SUCCESS");
var prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
prefs.setObject(mobile, forKey: "USERNAME")
prefs.setInteger(1, forKey: "ISLOGGEDIN")
prefs.synchronize()
self.presentViewController(OtpVC(), animated: true, completion: nil)
}
my OtpVC file is:
class OtpVC: UIViewController {
#IBOutlet weak var smsfield: UITextField!
#IBAction func continueButton(sender: AnyObject) {
}
}
The problem now is that when is login successful the page change and goes all black!
How i can fix that? Thanks in advance.
The page in blank is an indicator of that your view has not being loaded. And it seems like that's the case:
Please take a closer look, you are creating an instance of that class, but you are not instantiating the view of it.
In swift you can have single classes affecting several views in your
storyboards.
The correct way, should be:
if success == "1" {
NSLog("Login SUCCESS");
var prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
prefs.setObject(mobile, forKey: "USERNAME")
prefs.setInteger(1, forKey: "ISLOGGEDIN")
prefs.synchronize()
var storyboard = UIStoryboard(name: "Main", bundle: nil)
"Main" here should be the name of the storyboard in which the OtpVC view is.
var controller = storyboard.instantiateViewControllerWithIdentifier("OtpVC") as! OtpVC
"OtpVC" here should be the storyboard identifier of your view
self.presentViewController(controller, animated: true, completion: nil)
}
Update:
Another root cause may be that your identifier is not well set at storyboard.
At your Identity Inspector settings should be similar to:
Here in Module, like I have no such Class on my targets it says None.
This could be something you may want to have a look at. Maybe in your storyboard you are referencing other target than the one with such OptVC class implementation.
You can also use segues.
Set a segue from one view controller to the one where you want to shift in the storyboard.
Also set the identifier of the segue in storyboard.
Now use below code
self.performSegueWithIdentifier("identifier", sender: self)
self.presentViewController(OtpVC(), animated: true, completion: nil)
With the line above, you'll create a new instance of OtpVC programmatically. It's not a problem if that's what you want and the view controller is coded properly to be initialized like that.
But because it's all black I assume you've created OtpVC in storyboard so you'll either need to use a segue like Sukhdeep Singh Kalra has suggested or instantiate it with:
let destination = storyboard?.instantiateViewControllerWithIdentifier("identifier") as! OtpVC
presentViewController(destination, animated: true, completion: nil)
Both options, you'll have to set an identifier.
To set an identifier for the latter, go to storyboard and click on your view controller. Make sure the view controller is selected by clicking the left yellow button on top of your view controller. Then in identity inspector, type in the identifier name and replace "identifier" in my example with that name.