iOS Storyboard: Not able to instantiate view controller properly - ios

I'm really having trouble instantiating a custom view controller.
This is my storyboard set up. The third view controller is the one I'm trying to present.
I've tried both of these methods.
1: This results in black screen.
var searchController: SearchController = SearchController()
self.presentViewController(searchController, animated: true, completion: nil)
2: This results in a white, empty view controller popping up.
let mainStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let searchController : UIViewController = mainStoryboard.instantiateViewControllerWithIdentifier("searchController") as! UIViewController
self.presentViewController(searchController, animated: true, completion: nil)
Here is the code for the actual view controller:
class SearchController: UIViewController {
lazy var searchBar: UISearchBar = UISearchBar(frame: CGRectMake(0, 0, 200, 20))
override func viewDidLoad() {
super.viewDidLoad()
var leftItem = (UIBarButtonItem)(customView: searchBar)
self.title = "Search"
self.navigationItem.leftBarButtonItem = leftItem
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
It's very confusing because another custom view controller I am is presented properly while using method #1 above. Why wouldn't it work for this controller?
Thanks a lot everyone.

When using Storyboards you don't need to use presentViewController or instantiate your view controllers manually. Instead it's best to use a segue to move from one UIViewController to the next.
1. In your case, you want a show segue, which it looks like you've already done judging by your storyboard.
2. You need to give your segue an Identifier which you can do by selecting your segue and going to the Attributes Editor.
3. To perform your segue just call the following from your first view controller (instead of presentViewController).
self.performSegueWithIdentifier("YourIdHere", sender: self)
This will cause the storyboard to instantiate your SearchViewController and present it in the way that you've defined for that segue.
4. If you want to pass any values to your SearchViewController, you can override prepareForSegue in UIViewController. In your first view controller:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let searchViewController = segue.destinationViewController where segue.identifier == "YourIdHere") {
// Now you can set variables you have access to in `searchViewController`.
}
}
And that should be it!

Related

Presenting the new controller programatically in ios swift only displaying blank screen

I am developing a simple IOS application using swift. In my application I need to open new controller programatically from another controller. So I added another scene to the storyboard.
This is the screenshot
Then I added a new class for new controller which is inheriting from the UIViewController. This is the code for new controller
import UIKit
class ReplayController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
Then, I tried to open the new controller view (ReplayController) from the main controller like this in code.
func gameOver()
{
let replayController = ReplayController()
present(replayController, animated: true, completion: nil)
}
When I call that function, it just pops up the blank screen. Nothing appear on the screen. What is wrong and how can I solve it?
In your Storyboard file, ensure the ReplayController has its 'File's Owner' set to your ReplayController class. Then set the Storyboard ID like below:
Then you can load it like so:
let replay = storyboard?.instantiateViewController(withIdentifier: "ReplayController") as! ReplayController
self.present(replay, animated: true, completion: nil)
You have to reference it with id in storyboard
let replayController = self.storyboard?.instantiateViewController(withIdentifier: "replayControllerID") as! ReplayController
present(replayController, animated: true, completion: nil)
as this line only
let replayController = ReplayController()
doesn't load the xib or storyboard object associated with the VC
When you design your view controllers in storyboard, you need to programatically create them using the method instantiateViewController of class UIStoryboard to access it along with all the outlets.
Before that, you need to set the Storyboard ID of the respective view controller in your storyboard like this:
And then present it:
func gameOver() {
if let myVC = storyboard?.instantiateViewController(withIdentifier: "ViewController") {
self.present(myVC, animated: true, completion: nil)
}
}

Add Tab Bar Programmatically to ViewController

I have a TabBarController in my Main.storyboard file.
In my Upload.storyboard I am presenting a ViewController from the Main.storyboard file, however, it doesn't contain the tab bar.
The viewProfile button should go to a tab called Sharks and within that present a view controller based on data gathered in the Upload.storyboard (modal view).
Can I add the tab bar programmatically or am I not properly presenting the correct VC?
// MARK: - Actions
#IBAction func viewProfileButtonPressed(_ sender: UIButton) {
let stb = UIStoryboard(name: "Main", bundle: nil)
let sharkProfile = stb.instantiateViewController(withIdentifier: "sharkProfile") as! SharkProfileTableViewController
self.present(sharkProfile, animated: true) {
// add tab bar here?
}
}
What you need to do is present the tab bar view controller, not the view controller that is embedded in it
One method is to create a Delegate Protocol to allow a tap on View Profile button to "call back" to the View Controller that presented it. When that callback is received, the VC sets the "current" tab.
It will look something like this:
// define "call back" delegate protocol
protocol EncounterUploadedDelegate : class {
func didTapSharkProfileButton()
}
The Encounter view controller will need to conform to that protocol:
class EncounterViewController: UIViewController, EncounterUploadedDelegate {
// the normal stuff for this VC and all the other code for it
// ...
// conform to the protocol
func didTapSharkProfileButton() -> Void {
// when we get this call-back, switch to the Shark Profile tab
// tabs are zero-based, so assuming "SharkProfile" is
// is the 4th tab...
self.tabBarController?.selectedIndex = 3
}
// assuming a "Show Modal" segue named "ShowEncounterUploadSegue" is used
// to present the Modal View
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowEncounterUploadSegue" {
let vc = segue.destination as! TabModalVC
vc.encounterDelegate = self
}
}
}
The view controller to be presented as modal:
class TabModalVC: UIViewController {
weak var encounterDelegate: EncounterUploadedDelegate?
#IBAction func profileButtonTapped(_ sender: Any) {
// dismiss self (the modal view)
self.dismiss(animated: true, completion: nil)
// this will call back to the delegate, if one has been assigned
encounterDelegate?.didTapSharkProfileButton()
}
}
Hope that all makes sense :)

Goto another view programmatically in Swift 3

I'm using the following code to goto another view programmatically in swift 3. There is no error while running. But don't know why it is not going to that view
Code I used:
let images = self.storyboard?.instantiateViewController(withIdentifier:"Collection") as! UICollectionViewController
self.navigationController?.pushViewController(images, animated: true)
I want to goto CollectionView.swift
In order to navigate to between view controllers you use UINavigationController.
I will provide you a basic example of navigation, hopefully it will help you to make navigation work in your project.
Result
ViewController passes an image to DetailViewController between navigation:
Setting up your Views
First ensure that your root controller is embedded with a navigation controller control so that you can navigate using segues:
Connect your views that are being used to navigate.
Code
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// showDetail Segue
if segue.identifier == "showDetail" {
// Sending the image to DetailViewController
// Before appears in the screen.
let detailViewController = segue.destination as! DetailViewController
detailViewController.image = sender as? UIImage
}
}
#IBAction func loginButton(_ sender: AnyObject) {
// Go to another view controller
performSegue(withIdentifier: "showDetail", sender: imageView.image)
}
}
class DetailViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
var image: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
if let imageSent = image {
imageView.image = imageSent
}
}
}
I think it would be best instead of programmatically calling the Storyboard ID of the controller, to use Segues instead. You can find out how to use them here.
But if you have to use the SBID, here is an example snippet from one of my projects...
let vc = self.storyboard?.instantiateViewController(withIdentifier: "UpdateFilesViewController")
self.navigationController?.present(vc!, animated: true, completion: nil)
Notice that presenting might be the solution you are after instead of pushing. Additionally, you seem to be casting the UIViewController to a UICollectionViewController, which may cause problems as well. Keep note that a UICollectionViewController is a subclass of a UIViewController so the casting is unnecessary.
As we found out in the comments, your current viewController does not have a navigationController.
This means that
self.navigationController?.pushViewController(images, animated: true)
does nothing. You need to set up the initial viewController with a navigationController for this to have any effect.
Change your storyboard Identifier
You just need to change your storyboard identifier as "CollectionOfImages" in identity inspector as Storybroad identity so it will move on there.
Note : Please make sure that your current view controller is embed with navigation controller otherwise it will not push new controller during movement.
If you don't want to use navigation controller, you can simply present your controller using:
let images = self.storyboard?.instantiateViewController(withIdentifier:"Collection") as! UICollectionViewController
self.present(images, animated: true, completion: nil)

Why can't set a variable value of a viewController by using another viewController?

I know this is Duplicate Question, but i didn't find the solution for my problem. I have two controllers. HomeViewcontroller's variable of "myValue"'s value can't be set by using ViewController class.I don't know the issue here but code is working without error.But when i print "myValue" it shows nil.Destination view controller is not the controller that i want to set variable value. Destination view controller is a tab bar controller, so i want to set the variable value of first tab bar and second tab bar view controllers. This is my code :-
import UIKit
class ViewController: UIViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if(segue.identifier == "gotoHome") {
println("1111")
var storyboard = UIStoryboard(name: "Main", bundle: nil)
var viewController = storyboard.instantiateViewControllerWithIdentifier("HomeViewController") as! HomeViewController
viewController.myValue = 8888
}
}
}
this is the other view controller
import UIKit
class HomeViewController: UIViewController {
var myValue: Int!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
println(myValue)
}
}
You don't need to instantiate the viewcontroller, as it is already been done, you just need to fetch the viewcontroller for the current segue operation, so your code should look like below
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if(segue.identifier == "gotoHome") {
println("1111")
let controller = segue.destinationViewController as! UITabBarController
let homeController = controller.selectedViewController as !HomeViewController
homeController.myValue = 8888
}
}
Hope it helps.
Change to :
let viewController = segue.destinationViewController as! HomeViewController
You're printing the value of myValue in viewDidLoad which is called when you're instantiating it from your storyboard. You're setting myValue after creating your instance. Try to print myValue in viewWillAppear for example, then it should be set.

Skip/Add a View Controller in Navigation Stack

I have three View Controllers embedded in Navigation Controller. I want to go from VC1 to VC3 so that in VC3 the Navigation Item's back button would direct the user to VC2 instead of VC1. I think this should be done either by adding the VC2 to the Navigation Stack between VC1 and VC3 when VC3 is created or by skipping the second View Controller.
I tried this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case "JumpToThirdVCSegue":
if let secondvc = segue.destinationViewController as? SecondViewController {
secondvc.performSegueWithIdentifier("ThirdVCSegue", sender: self)
}
default: break
}
}
}
but I couldn't get it working. Perhaps performing a segue isn't possible when the View Controller isn't open yet?
What is the best way to skip a View Controller / add a View Controller in the middle of Navigation Stack? I hope this helps you to understand what I'm trying to do:
Something like this should work:
self.navigationController?.pushViewController(viewController2, animated: true)
self.navigationController?.pushViewController(viewController3, animated: true)
EDIT:
If you want to push the second view controller without being noticed by the user you need to add it to the navigation controller after the third view controller is pushed. This can be done by implementing UINavigationControllerDelegate. You can store your second view controller in a variable and insert it to the navigation controllers hierarchy in delegate method. Your main view controller will look like following:
class MyViewController: UIViewController, UINavigationControllerDelegate {
var viewControllerToInsertBelow : UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.delegate = self
}
func pushTwoViewControllers() {
if let viewController2 = self.storyboard?.instantiateViewControllerWithIdentifier("id1"),
let viewController3 = self.storyboard?.instantiateViewControllerWithIdentifier("id2") { //change this to your identifiers
self.viewControllerToInsertBelow = viewController2
self.navigationController?.pushViewController(viewController3, animated: true)
}
}
//MARK: - UINavigationControllerDelegate
func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) {
if let vc = viewControllerToInsertBelow {
viewControllerToInsertBelow = nil
let index = navigationController.viewControllers.indexOf(viewController)!
navigationController.viewControllers.insert(vc, atIndex: index)
}
}
}
if var navstack = navigationController?.viewControllers{
navstack.append(contentsOf: [vc1,vc2])
navigationController?.setViewControllers(navstack, animated: true)
}
Works well
Edit: using swift and segues, this should work:
override func performSegueWithIdentifier(identifier: String?, sender: AnyObject?) {
super.performSegueWithIdentifier(identifier, sender: sender);
if identifier == "JumpToThirdVCSegue" {
// now the vc3 was already pushed on the navigationStack
var navStackArray : [AnyObject]! = self.navigationController!.viewControllers
// insert vc2 at second last position
navStackArray.insert(viewController2, atIndex: navStackArray.count - 2)
// update navigationController viewControllers
self.navigationController!.setViewControllers(navStackArray, animated:false)
}
}
So you override the performSegueWithIdentifier in the VC1 in order to change the navigation stack when the segue to VC3 has actually been performed (and is not only in preparation).
This assumes, that you want to handle this special navigation logic in VC1.
You can use method
func setViewControllers(_ viewControllers: [UIViewController],
animated: Bool)
Here's the documentation (link) that solves the UIViewController skipping problem.
Simply pass all the needed UIViewControllers to it (in navigational order) and the last one will be made as the current active UIViewController (if it already isn't the active one).
My solution would be to keep a BOOL property of when you should skip to third and when not skip, like declaring ’shouldSkip’ in VC2 so that if u set it in prepare segue like below you can act according to that in VC2
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case "JumpToThirdVCSegue":
secondvc.shouldSkip=true
}
default: break
}
}
}
and then in viewDidload VC2 you should check this BOOL and if it is true and perform segue and if not just move on
you could also pass pass whenever that kind of skip is not necessary
Building off of MarkHim's answer, I was getting a Black screen when navigating back to the inserted view controller so I came up with the following solution.
BTW - I wanted to insert the new view controller directly under the view controller I just segued to hence the navStack.count - 1 instead of navStack - 2.
Here is my solution:
override func performSegue(withIdentifier identifier: String, sender: Any?) {
super.performSegue(withIdentifier: identifier, sender: sender)
if identifier == "JumpToThirdViewControllerSegue",
let navController = navigationController { // unwrap optional
// initialize the view controller you want to insert
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewControllerToInsert = storyboard.instantiateViewController(
withIdentifier: "SecondVC") as! SecondViewController
// set any passed properties
viewControllerToInsert.passedProperty = propertyToPass
// create an object using the current navigation stack
var navStackArray: [UIViewController]! = navController.viewControllers
// insert the newly initialized view controller into the navStackArray
navStackArray.insert(viewControllerToInsert, at: navStackArray.count - 1)
// replace the current navigation stack with the one you just
// inserted your view controller in to
navController.setViewControllers(navStackArray, animated: false)
}
}

Resources