Goto another view programmatically in Swift 3 - ios

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)

Related

viewDidLoad is not being called while pushing a viewController

Here am trying to navigate to another viewController by clicking on a button.
It is navigating to next viewController, but viewDidLoad() is not calling here
Here is the code which i wrote to navigate to another viewController on clicking on a button
#IBAction func nextButtonClicked(_ sender: Any) {
let OrdersVC = self.storyboard?.instantiateViewController(withIdentifier: “LoginViewController") as! LoginViewController
self.navigationController?.pushViewController(OrdersVC, animated: true)
}
and here is my viewController (which i need to navigate)
#IBOutlet weak var activeButton: UIButton!
#IBOutlet weak var upcomingButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
Here am able to get into the class, but viewDidLoad() itself it is not calling.
How should i achieve this ?
[![Here is my storyboard][1]][1]
Your problem is that the class is not settled in IB for that VC; make sure class you load and storyboard ID matches the one you want to load. For example, to load:
let OrdersVC = self.storyboard?.instantiateViewController(withIdentifier: “secondID") as! secondViewController
self.navigationController?.pushViewController(OrdersVC, animated: true)
Image:
Two things come to my mind when looking at your question:
Check that the nextButtonClicked method is connected to the button (the code there looks OK, so maybe it is just not executed).
Check if the viewDidLoad that you are speaking about is really in LoginViewController that you instantiate (and I hope you are testing the fact that viewDidLoad is called by setting a breakpoint on super.viewDidLoad()).
Just write folloiwng line in between your push code
#IBAction func nextButtonClicked(_ sender: Any) {
let OrdersVC = self.storyboard?.instantiateViewController(withIdentifier: “LoginViewController") as! LoginViewController
_ = OrdersVC.view // write this line
self.navigationController?.pushViewController(OrdersVC, animated: true)
}
It often happens when the identifier with which you instantiate your ViewController from the storyboard is incorrect. For e.g.
[[self getStoryboard] instantiateViewControllerWithIdentifier:MyVC];
If MyVC is the identifier of some other ViewController, this might happen.

Delegate/Protocols Passing data from one view controller to another

Trying to pass data from one view controller MainScreenVC to Another RatesVC with protocol and extension, but that's not working, app crashing everytime . I'm clearly see that problem with code on second VC(because print showing correct data after action on first VC) but not sure where is error.
StoryBoard and 1st VC Example
Second VC
1st View controller
import UIKit
protocol transferNameOfCurrency {
func currencySelected(nameOfCurrency: String)
}
class MainScreenVC: UIViewController {
var transferCurrencyDelegate: transferNameOfCurrency?
var nameOfTheCurrency: String?
#IBAction func updateRates(_ sender: Any) {
nameOfTheCurrency = "EUR"
transferCurrencyDelegate?.currencySelected(nameOfCurrency:
nameOfTheCurrency)
print(nameOfTheCurrency)
}
}
2nd ViewController
import UIKit
class RatesVC: UIViewController {
var currencySelected: String?
override func viewDidLoad() {
super.viewDidLoad()
if let push = self.storyboard?.instantiateViewController(withIdentifier: "MainScreenVC") as? MainScreenVC
{
push.transferCurrencyDelegate = self
}
// Do any additional setup after loading the view.
}
}
extension RatesVC: transferNameOfCurrency {
func currencySelected(nameOfCurrency: String) {
currencySelected = nameOfCurrency
print(currencySelected)
}
}
The most obvious problem lies here:
if let push = self.storyboard?.instantiateViewController(withIdentifier: "MainScreenVC") as? MainScreenVC {
push.transferCurrencyDelegate = self
}
You have to realize that instantiateViewController creates a new view controller - it's not the reference to the view controller presented at the screen. In that code you just created a completely new view controller and then set its delegate to self, but otherwise nothing else.
Without knowing the context it is really hard to suggest anything - prepare(for:) segue might be the place where you want to set the delegate. Anyway, the problem is that you have to obtain a reference to the controller that is presented on the screen, the one that is supposed to be reacting to those events.
Moreover, from the memory management aspect, you should really consider making the delegate property a weak one to prevent memory leaks.
EDIT
So after seeing the minimal working example you provided at link, I think I can provide the solution on how to get that string to the SecondVC.
Your first view controller with comments:
import UIKit
class ViewController: UIViewController {
var newLine: String = "EUR"
#IBAction func push(_ sender: Any) {
// here the secondVC does not exist yet, calling delegate.transferWord() here would have no sense
// performSegue will create that secondVC, but now it does not exist, nor it is set up as the delegate
self.performSegue(withIdentifier: "ViewController", sender: navigationController)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let secondVC = segue.destination as? SecondVC, segue.identifier == "ViewController" {
// at this moment secondVC did not load its view yet, trying to access it would cause crash
// because transferWord tries to set label.text directly, we need to make sure that label
// is already set (for experiment you can try comment out next line)
secondVC.loadViewIfNeeded()
// but here secondVC exist, so lets call transferWord on it
secondVC.transferWord(word: newLine)
}
}
}
No need for delegates here, because your ViewController is the one pushing the SecondVC to the Navigation controller - that means that you can access it directly in prepare(for:), as you can see above.
Now the SecondVC is super simple (I omitted unnecessary code):
import UIKit
class SecondVC: UIViewController {
#IBOutlet weak var label: UILabel!
func transferWord(word: String) {
label.text = word
}
}
Storyboards can stay as they are.

How to trigger action on embedded ContainerViewController from ViewController and how to do it inverse?

does anyone know how to pass an action from ViewController to its ContainerViewController. I try to hide the container view by an action that is triggered by itself. The ContainerViewController is embedded in the container view.
ViewController:
#IBOutlet weak var ChoseLanguageContainer: UIView!
**ContainerViewController:**
#IBAction func action(_ sender: Any) {
ViewController().containerView.isHidden = true
} //I know this does not work
I had the similar requirement, I created my own delegate methods which were implemented in ContainerViewController.
protocol ContainerViewControllerDelegate :class{
func notifyItemChange(any params you need to pass.)
}
In my container ViewController, I created a variable for delegate.
var changeContainerDelegate :ContainerViewControllerDelegate?
In my parent View Controller which contains the container, I did the following.
To get the instance of view controller which is embedded in the container.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let containerViewController = segue.destination as? ContainerViewController{
containerViewController.changeContainerDelegate = self
}
Create IBOutlet of container view in your view controller.
Conform to this protocol and write the implementation in View controller.
func changeContainerVC(containerVCName : String ,dataToBePassed:[AnyObject]?) {
containerView.isHidden = true
}
In the button action inside the ContainerViewController call the delegate like :
changeContainerDelegate?.notifyItemChange()
This works for me. Hope helps ou too!
In your code:
ViewController().containerView.isHidden = true
You are making a new instance of the ViewController and using it to hide the containerView. This won't work. Instead you need to hide the containerView of the current instance of ViewController, i.e self.
Here is the code you can try:
class ViewController: UIViewController
{
#IBOutlet weak var containerView: UIView!
override func viewDidLoad()
{
super.viewDidLoad()
}
#IBAction func hideContainerView(_ sender: UIButton)
{
self.containerView.isHidden = true
}
}
View Hierarchy:
Let me know if you face any other issue regarding this.

How to perform segue from container view within a view displayed by navigation controller?

I want to segue from a view container within "H" that is presented using the navigation controller connected to the Split View Controller. How can I accomplish this? I have tried regular performSegueWithIdentifier using locally linked storyboard ID's but that removes the top navigation bar. I want to retain the top navigation bar and execute the segue as if it was done using the master navigation controller (rows that select which view controller is being presented in the detail view).
Any help is greatly appreciated!
Here is an example of how to perform a segue from an embedded ViewController.
ViewController.swift
import UIKit
protocol SegueHandler: class {
func segueToNext(identifier: String)
}
class ViewController: UIViewController, SegueHandler {
func segueToNext(identifier: String) {
self.performSegueWithIdentifier(identifier, sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EmbedH" {
let dvc = segue.destinationViewController as! HViewController
dvc.delegate = self
}
}
}
HViewController.swift
import UIKit
class HViewController: UIViewController {
weak var delegate: SegueHandler?
#IBAction func pressH(sender: UIButton) {
delegate?.segueToNext("GoToGreen")
}
}
Setup:
Use delegation to have the HViewController tell its embedding viewController to perform the segue.
Create a protocol called SegueHandler which just describes a class that implements the method segueToNext(identifier: String).
protocol SegueHandler: class {
func segueToNext(identifier: String)
}
Make your viewController implement this protocol by adding it to the class declaration line:
class ViewController: UIViewController, SegueHandler {
and by implementing the required function.
Add a delegate property to HViewController:
weak var delegate: SegueHandler?
Click on the embed segue arrow between ViewController and HViewController. Give it the identifier "EmbedH" in the Attributes Inspector.
Create a show segue between ViewController and the GreenViewController by Control dragging from the viewController icon at the top of ViewController to the GreenViewController. Name this segue "GoToGreen" in the Attributes Inspector.
In prepareForSegue for ViewController, when the "EmbedH" segue happens, set the delegate property of HViewController to self (ViewController).
When the user clicks the H button in the HViewController, call delegate?.segueToNext("GoToGreen") to trigger the segue in ViewController.
Here it is running in the simulator:
I was needing exactly what #vacawama proposed here, though I couldn't reproduce that, I tried exactly your steps but self.delegate?.segueToNext("GoToGreen") got called but neither the protocol itself nor the container view controller. After an entire day searching about this approach I realized the problem was with the swift version. Just replace this:
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EmbedH" {
let dvc = segue.destination as! HViewController
dvc.delegate = self
}
}
for this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EmbedH" {
let dvc = segue.destination as! HViewController
dvc.delegate = self
}
}
Other detail I was missing was about the embedded segue. Be sure to connect the container View to the HViewController, not the View Controller itself, otherwise the Embed option for segue won't appear.

iOS Storyboard: Not able to instantiate view controller properly

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!

Resources