I am sure I have two view controllers, that have the appropriate files as their classes. (ViewController1.swift and ViewController2.swift) I am trying to pass data from one of the view controllers to the other one with the following code; but I get the error "UIView Controller is not convertible to ViewController2". What should I do? I tried to change as to as! and var to let; neither worked.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!){
if (segue.identifier == "btnSubmitSegue") {
var svc = segue.destinationViewController as ViewController2;
svc.DataPassed = textField.text
}
}
the code for ViewController2
import UIKit
class ViewController2: UIViewController {
var toPass: String = ""
#IBOutlet weak var labeld: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.labeld.text = "passed: \(self.toPass)"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
the code for ViewController1
import UIKit
class ViewController1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var textField: UITextField!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "TheSegue" {
let vc = segue.destinationViewController as! ViewController2
vc.toPass = self.textField.text!
}
}
}
The following example works fine for me. Code for the first view controller:
class ViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "TheSegue" {
let vc = segue.destinationViewController as! ViewController2
vc.passedText = self.textField.text!
}
}
}
And in the second view controller:
class ViewController2: UIViewController {
var passedText: String = ""
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.label.text = "passed: \(self.passedText)"
}
}
Don't forget to name the segue in the Storyboard:
Your destination view controller is referring to the wrong controller. You should have this in your code, not as! ViewController1:
let vc = segue.destinationViewController as! ViewController2
Also need to fix your IBOutlet name, the vc.toPass = self.textField.text! should be:
vc.toPass = self.labeld.text!
Make sure the custom class is set for the second view controller:
It may just be that ViewController2 is not declared as a subclass as UIViewController.
At the top of the ViewController2 file you would declare it as follows:
class ViewController2: UIViewController {
\\ ...
}
Have you set the name in the Storyboard ? Under custom class, Class needs to point to the view controllers associated class.
this class is not key value coding-compliant for the key
labelPassedData.' But I do not even have a variable like
labelPassedData
Click you view controller and press right hand arrow button, it shows all connections.
I guess you copy and pasted the view controllers and hence you get this problem.
Related
I try to access a textfield content in my view profileView from my first view viewController but it doesn't work.
I read that I have to use override func prepare(for segue: UIStoryboardSegue, sender: Any?) {} so I did but it still not working and I don't understand why.
Here is my viewController code:
// This code is the code generated by xCode, only the loginField was added.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var loginField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
and here is my profileController prepare func:
var login: String!
#IBOutlet weak var myLabel: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("prepare")
if (segue.identifier == "mainMenu")
{
let svc = segue.source as! ViewController
login = svc.loginField.text
self.myLabel.text = login
print(login)
}
}
Actually, the thing i really read was "prepareForSegue" func but when I try to override it, xCode won't compile the code...
Thanks for your help.
write your prepareforSegue method in your viewController
why?
Because you want to send ViewController textfield data to profileViewController.That is write your prepareForSegue in Source. Here,
Source = ViewController
Destination = ProfileView
Note:
Use a variable in your destinationViewController(ProfileView Controller) and keep the textField data in this variable.
like this in prepareForSegue.
destinationVC.someVariable = self.SourceViewLabel.text // your sourceViewController
and in viewDidLoad of your destinationViewController(ProfileView) write this.
self.profileLabel.text = somVariable
In my first view i wrote this.
#IBAction func nexVCAction(_ sender: Any) {
self.performSegue(withIdentifier: "secondvc", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "secondvc" {
let vc = segue.destination as! secondVC
vc.stringHolder = textField.text!
}
}
And this is the second view code.
import UIKit
class secondVC: UIViewController {
#IBOutlet weak var textlabel: UILabel!
var stringHolder:String = ""
override func viewDidLoad() {
super.viewDidLoad()
textlabel.text = stringHolder
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I made git project for this.
Check only FirstVC.swift and secondVC.swift
And this is the output.
You cannot set the text of the UITextField and any other components of UIKit in the prepare for segue, because all View components of the recently allocated controller are not initialised yet they are all nil in prepareForSegue. they allocated only be when the viewController is presented.
You set Text for current ViewController TextField at prepareForSegue. Instead of this you should set text at viewWillAppear method while pop or dismiss the presented ViewController by using Notification, Delegate or userDefaults etc...
You just need a small fix since UITextField and any other components of UIKit are not initialised yet in the prepare for segue:
var login: String!
#IBOutlet weak var myLabel: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("prepare")
if (segue.identifier == "mainMenu")
{
let svc = segue.source as! ViewController
login = svc.loginField.text
// self.myLabel.text = login
print(login)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.myLabel.text = login
}
Data flow from one controller to another is the very popular theme for ios beginners. There are several approaches to achieve this goal: segues, delegate pattern, notifications, initialization of new instance of controller, saving data to local storage etc.
You should user prepare(for segue: sender:) method only when you have segue from you current controller to another. You can get access to destination controller instance via destination property of segue parameter.
Also never call IBOutlet properties of destination controller in prepareForSegue - it can be cause of crash, because Outlets may be uninitilized at this time. Instead use String property for example.
Here is sample code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "mainMenu") {
if let destinationVC = segue.destination as? ViewController {
destinationVC.login = self.myLabel.text
}
}
}
Where login in destinationVC is the String variable to store login.
I have two view controller.
I have one test method in first view controller as follow
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
myLabel.hidden = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func showLabel(){
myLabel.hidden = false
}
}
If i call showLabel from another class it give me fatal error: unexpectedly found nil while unwrapping an Optional value
Another viewController is as follow
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func Save(sender: AnyObject) {
ViewController().showLabel()
}
}
If i call showLabel Method from ViewController the its works fine but if i call it from SecondViewController then i got error.
Both your view controllers use storyboard/xib; when you create such view controller instance in code (as you do in Save function) you need use UINib class or instantiateViewControllerWithIdentifier method of UIStoryboard class, since default initializers (like init() you used for ViewController) know nothing about outlets and storyboard/xib associated files (outlet property myLabel will always be nil).
If your code not just template, then you should refactor it, because performing UI changes in view controller that not presented on screen (and never will be) not have sense
It's because your label "myLabel" is always nil.
Your label is part of your first ViewController. The label loads when the view loads. Since the ViewController view has not loaded, the label has not loaded either, and therefore will be nil
You need use delegate:
1-Creat a protocol with a function describes with you are planning to do.
protocol ViewControllerDelegate: AnyObject {
func showLabel()
}
2-Add a delegate in your SecondViewController with the type if the protocol ViewControllerDelegate.
class SecondViewController: UIViewController {
weak var delegate: ViewControllerDelegate? // add delegate
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func Save(sender: AnyObject) {
delegate?.showLabel() // call the method from delegate
}
}
3-Implement the ViewControllerDelegate in your ViewController.
class ViewController: UIViewController, ViewControllerDelegate {
3-Set the delegate in the first view controller inside prepareForSegue:
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
myLabel.hidden = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func showLabel(){
myLabel.hidden = false
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let viewController = segue.destinationViewController as? SecondViewController {
viewController.delegate = self
}
}
}
just add class before function:
class Math{
class func addition(numberOne: Int, numberTwo: Int) -> Int{
var ans: Int = numberOne + numberTwo
return ans
}
}
Then you can call it from another Swift class like this:
Math.addition(40, numberTwo: 2)
To assign it to a variable i:
let i = Math.addition(40, numberTwo: 2) // -> 42
It shows that the source view controler does not have the string variable so what should I do?
This is the code in my source view controller
#IBOutlet var textfield: UITextField!
#IBOutlet var tofirstbutton: UIButton!
#IBOutlet var tosecondbutton: UIButton!
var s:String!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "unwindtosecond"{
self.s=self.textfield.text
}
}
and this is the code in my destination view controller
#IBOutlet var textfield: UITextField!
#IBOutlet var tofirstbutton: UIButton!
#IBOutlet var tothirdbutton: UIButton!
#IBOutlet var label: UILabel!
#IBAction func unwindtosecond(Segue:UIStoryboardSegue){
if Segue.identifier == "unwindtosecond" {
var source:ViewController = Segue.sourceViewController as ViewController
var s:String = source.s!
}
I am getting an error saying that viewcontroller does not have a member named s.
Here is some example code to implement a main controller segueing to a second VC, and the second unwinding to the main controller. Notice that in the function the second cVC unwinds to, you can refer to the second controller and access a variable in the second controller. If you want to initialize a variable in the second VC from the main VC, you can use a prepareForSegue in the main VC.
import UIKit
class ViewController: UIViewController {
var varInMainVC = ""
#IBAction func segueToSecondVCAction(sender: UIButton) {
performSegueWithIdentifier("segueToSecondVC", sender: self)
}
#IBAction func unwindFromSecondVC(segue: UIStoryboardSegue) {
// return here from second VC
let vc = segue.sourceViewController as! SecondViewController
varInMainVC = vc.varInSecondVC
println("varInMainVC = \(varInMainVC)")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
import UIKit
class SecondViewController: UIViewController {
var varInSecondVC = ""
var varLoadedFromMainVC = 0
override func viewDidLoad() {
super.viewDidLoad()
varInSecondVC = "Test String to be returned"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Try these solutions:
Protocol delegate: https://stackoverflow.com/a/24299231/1925852
Setup a closure as a property: https://stackoverflow.com/a/24318588/1925852
Why can't I pass data from one view controller class to another view controller class when they are in the same .swift file?
I've done both of the tutorials linked bellow in an effort to understand passing data, and both have you make another .swift file for you second view controller. When I followed them exactly it works. When I do everything the same but just put the class SecondViewController in the same .swift file as the class FirstViewController it doesn't work. Why?
I'm building an application with three view controllers and I'd rather just keep all the code in one file if I can. Is this why it's not working or is it just bad form?
Tutorials:
How to Pass Data from View Controller (Swift : Xcode) on YouTube
Segue Between Swift View Controllers on CodingExplorer
This works:
// ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet var TextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var DestViewController: ViewTwo = segue.destinationViewController as ViewTwo
DestViewController.LabelText = TextField.text
}
}
// ViewTwo.swift
import UIKit
class ViewTwo: UIViewController {
#IBOutlet var Label: UILabel!
var LabelText = String()
override func viewDidLoad() {
Label.text = LabelText
}
}
This does not:
// ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet var TextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var DestViewController: ViewTwo = segue.destinationViewController as ViewTwo
DestViewController.LabelText = TextField.text
}
}
class ViewTwo: UIViewController {
#IBOutlet var Label: UILabel!
var LabelText = String()
override func viewDidLoad() {
Label.text = LabelText
}
}
Working fine for me.
Have you set the classes for your views properly? The view with your textField (and probably a button to ViewTwo) should be set to ViewController and your second view with the label should be set to ViewTwo.
Check the connections between your label/textField. Are they properly connected to your class?
This is the code I used, which should be exactly the same:
import UIKit
class ViewController: UIViewController {
#IBOutlet var TextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var DestViewController = segue.destinationViewController as! ViewTwo
DestViewController.LabelText = TextField.text
}
}
class ViewTwo: UIViewController {
#IBOutlet var Label: UILabel!
var LabelText = String()
override func viewDidLoad() {
Label.text = LabelText
}
}
I new to swift language and know this question is duplicated.I`ve found several similar question and answer, but i could not able to figure out the problem.
I want to pass the value of detectionString variable to ResultViewController from ScanViewController.
ScanViewcontroller as below:
import UIkit
class ScanViewController: UIViewController {
var detectionString : String!
override func viewDidLoad() {
super.viewDidLoad()
detectionString = “SomeDetectedString”
}
override func prepareForSegue(segue: UIStoryboardSegue,sender: AnyObject!) {
if (segue.identifier == "MySegue") {
var svc = segue.destinationViewController as ResultViewController;
svc.detectedString = detectionString
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
The ResultViewController as below:
import UIkit
class ResultViewController: UIViewController {
var detectedString: String!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor=UIColor.whiteColor()
println(detectedString)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
The println(detectedString) gives me no result. question is that how can get the variable from ScanViewController?
This may sound bizarre but there is nothing wrong with your code BUT it does not work as is. I used your code identically and it ignored the segue. Then I embedded ScanViewController in a navigation controller in the storyboard. I also put a call to self.performSegueWithIdentifier("MySegue", sender: self) in the ScanViewController viewDidLoad to initiate the segue. Then everything works like a charm. Your prepareForSegue is fine. Yuvrajsinh's suggestion is fine but not necessary (I tried it after changing DetailVC to ResultViewController). Without the navigation controller nothing works. segue.identifier is a string and it will work in a straight Swift string comparison.
Here is the code for ScanViewController:
import UIkit
class ScanViewController: UIViewController {
var detectionString : String!
override func viewDidLoad() {
super.viewDidLoad()
detectionString = "SomeDetectedString"
println(detectionString)
self.performSegueWithIdentifier("MySegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue,sender: AnyObject!) {
if (segue.identifier == "MySegue" || segue.identifier == "SegueFromButton") {
println("prepareForSegue")
var svc = segue.destinationViewController as ResultViewController;
svc.detectedString = detectionString
println("svc.detectedString: \(svc.detectedString)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
and for ResultViewController:
import UIkit
class ResultViewController: UIViewController {
var detectedString: String!
override func viewDidLoad() {
println("Result Load View")
super.viewDidLoad()
self.view.backgroundColor=UIColor.whiteColor()
println("detectedString in Result: \(detectedString)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
your segue.identifier == "MySegue" is some how not comparing in the way it should.
replace your code with following function and you are done.
override func prepareForSegue(segue: UIStoryboardSegue,sender: AnyObject!) {
let segueName: String = segue.identifier!;
if (segueName == "MySegue") {
var svc = segue.destinationViewController as DetailVC;
svc.detectedString = detectionString
}
}
There are three options for you:
use prepareForSeguemethod to get the target ViewController and set up the property
set up delegate relationship between the two ViewControllers to communicate
set up Observer with NSNotificationCenter