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.
Related
My sender class for delegation:
import UIKit
protocol tapDelgation:class {
func tapConfirmed(message:String)
}
class ViewController: UIViewController {
weak var delegate:tapDelgation?
#IBAction func deligateSenderAction(_ sender: Any) {
var data = "hello world"
print(data)
self.delegate?.tapConfirmed(message: data)
}
}
My reciever class:
import UIKit
class NextViewController: UIViewController {
weak var vc:ViewController? = ViewController()
override func viewDidLoad() {
super.viewDidLoad()
vc?.delegate = self
}
}
extension NextViewController : tapDelgation {
func tapConfirmed(message: String) {
print(message)
}
}
What is expected: A button on sender vc is pressed and from reciever vc a console print would be popped. But in vain, nothing happens. Does any one know why it is happening? If it is not possible then why?
It looks like a memory management problem to me.
First problem: Creating a view controller with a default initializer like ViewController() is almost never the right thing to do. because it won't have any view contents.
You don't explain how your NextViewController and your ViewController get created and displayed.
It looks like NextViewController has a weak reference to ViewController, and ViewController's delegate point is also weak (delegate references should almost always be weak.)
This line:
weak var vc:ViewController? = ViewController()
Will cause NextViewController to create an instance of ViewController that isn't owned by anybody, so it will immediately be deallocated and the vc variable will go back to being nil. By the time you get to NextViewController's viewDidLoad, vc will be nil, so the optional binding in the line vc?.delegate = self won't do anything.
NextViewController's vc variable should almost certainly be a strong reference, not weak, but you don't show how ViewController ever gets displayed to the screen, so it isn't clear what you're trying to do.
weak var vc:ViewController? = ViewController()
Remove weak if you don't set the vc somewhere else and any other instance doesn't keep a strong reference to it.
If there is another instance with a strong reference, please share the related code.
The answer from the https://stackoverflow.com/users/205185/duncan-c is totally correct unless there is any other code which affects the presentation of the NextViewController and reference to the vc: ViewController
I changed viewController to SenderViewController but no luck and Sender and receiver is connected via navigation controller. i.e. If i press a button on sender a recieve comes via push transition. my aim was to since it is triggered an IBAction then the second view controller would implements the tap confirmed function. thanks for your answer. Learned a lot :)
Due to this comment, you need to implement prepareForSegue() method in your ViewController (original one) and set the vc property of the "next" view controller there instead of = ViewController() in the "next" make the extension on the ViewController:
extension ViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let nextController = segue.destinationViewController as! NextViewController
nextController.vc = self
}
}
Explanation based on the comment:
You get a new instance of the NextViewController with the new instance of the ViewController instantiated on its init (instead of passing the original instance of ViewController to it). That's where you can ge a strange behaviour with delegation.
weak var vc:ViewController? = ViewController()
Remove weak for vc it will release the view controller memory after disappear
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.
App has two View Controllers: ViewController (this is the main View Controller that displays the majority of the app's content) and SecondViewController (accessible via a UIButton on ViewController; SecondViewController is only used to display a user's inventory, and a UIButton within SecondViewController allows the user to return to the original view, ViewController). Currently, the app uses the "Show" action segue to switch between View Controllers when the user presses the appropriate UIButton. However, after switching from ViewController to SecondViewController, and then pressing the UIButton to return to ViewController, the properties of ViewController have been reverted to the properties that occur when the app launches (background color is changed, certain text fields appear that shouldn't).
So, how do I "save the state" of ViewController when the user moves to SecondViewController, so that the user resumes where they left off when they return to ViewController?
What you are looking for is an unwind segue. Here's the simplest way of how to create it:
In your ViewController (or, basically any other view controller you are willing to pop to) create an IBAction that accepts an instance of a segue (function name doesn't really matter):
#IBAction func unwindToThisVC(segue: UIStoryboardSegue) { }
In the storyboard, go to SecondViewController, and control + drag from your UIButton to the Exit outlet of ViewController and then select the IBAction you've created in step 1:
More on Unwind Segues
The way you are doing it now (using Show from the second to get back to the first) actually brings up a third VC.
What you want to do is dismiss the second view controller.
The normal way is to implement a protocol for the second one that the first one implements and then to have a function in that protocol for the second one to let the first one know it is done.
When the function is called, the first one dismisses the second and then it will be shown again with its state intact.
Here is a simple example of segue and unwind that you can adapt to your problem... Assume that you have ViewController with label and a button and a SecondViewController with label and a button.
For the first ViewController...
import UIKit
//steps to receive data back from SecondViewController...
//1. create protocol in the SecondViewController (see SecondViewController code)
//2. conform to the protocol
class ViewController: UIViewController, UnwindSegue {
//3. method that gets triggred.
func dataReceived(dataSegued: String) {
labelOne.text = dataSegued
}
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var labelOne: UILabel!
var textReceived : String = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btPressed(_ sender: Any) {
performSegue(withIdentifier: "goToSecondController", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToSecondController" {
let destinationVC = segue.destination as! SecondViewController
destinationVC.textSegued = textField.text!
//4. create delegate in the SecondViewController (see SecondViewController code)
//5. set ourselves up as delegate of SecondViewController
destinationVC.delegate = self
//6. then dismiss the SecondViewController (see SecondViewController code)
}
}
}
Then for your SecondViewController...
import UIKit
//1. create protocols and delegates to transfer data back
protocol UnwindSegue {
//single required method with a single parameter
func dataReceived(data:String)
}
class SecondViewController: UIViewController {
var textSegued : String?
//4. create delegate of the protocol of type CanReceive that can be a nil. If it is nil, it doesn't go anywhere when BT is pressed
var delegate : UnwindSegue?
#IBOutlet weak var label: UILabel!
#IBOutlet weak var secondTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
label.text = textSegued
}
#IBAction func btTwoPressed(_ sender: Any) {
//this is not triggered if var delegate is nil (as defined as optional)
delegate?.dataReceived(data: secondTextField.text!)
//6. dismiss the 2nd VC so you can see the fist VC
dismiss(animated: true, completion: nil)
}
}
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)
I have the following setup:
StartViewController has a ContainerView that contains ContainerViewController
I try to find a way to hidden an element in StartViewController after a task is performed in ContainerViewController.
For this I try to use delegation method like this:
StartViewController
class StartViewController: UIViewController, showBannerAdDelegate {
#IBOutlet weak var bannerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
bannerView.hidden = false
}
func bannerAdHidden(status: Bool) {
bannerView.hidden = status
}
}
ContainerViewController
protocol showBannerAdDelegate: class {
func bannerAdHidden(status: Bool)
}
class ContainerViewController: UIViewController {
weak var delegate: showBannerAdDelegate! = nil
#IBAction func buttonPressed(sender: UIButton) {
delegate.bannerAdHidden(true)
}
}
If I presented the ContainerViewController I could do in prepareForSegue
let destination = segue.destinationViewController as! ContainerViewController
destination.delegate = self
But in this case both View Controller are always present.
What code should I add to the View Controller to make it work?
Thank you,
If one of the view controllers is inside a container view then it is loaded with an embed segue, which fires when the containing view controller is first loaded. The prepareForSegue method still gets called, so you can set up a delegate exactly as you've described. I always thought embed segues were a little odd (it's not really a segue, more like loading a child view controller) but that's how it works.