Retrieving a variable from another view controller - swift

The variable latitude in ViewController1 is visible. Why is the variable from another ViewController empty? Whenever I run the code the .text property of ActualCoordinatesText label is empty...
class ViewControllerGpsMaps: UIViewController {
#IBOutlet weak var ActualCoordinatesText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func GetCoordinates(_ sender: Any) {
GetActualCoordinates()
}
public func GetActualCoordinates() {
let sb = storyboard?.instantiateViewController(withIdentifier: "ViewController1") as! ViewController
ActualCoordinatesText.text = sb.latitude
}
}
Thanks for the help!

You are recreating a new instance of ViewController1 that means that the data included is the initialized value...
If your ViewControllerGPSMaps is called by VieController1 you should use the prepare(for segue:, sender:) of the ViewController1 to "give" the data you want to transfer...

Related

How can I pass data from a parent view controller to an embedded view controller in Swift?

I have a view controller embedded in another VC.
I would like to get the value of a variable from the main VC inside the embedded one. Specifically, I would like to change the text of label2 based on the value of label1.
I tried with "prepareForSegue", but it seems it's not triggered for embedded view controllers. I tried to isolate the problem in a test project:
Code for main VC:
class MyViewController: UIViewController {
#IBOutlet weak var label1: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label1.text = "Hello"
}
}
Code for embedded VC:
class EmbeddedVC: UIViewController {
#IBOutlet weak var label2: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
Thanks for your help :)
A way to achiŠµve this is to get the child view controller instance in the parent's viewDidLoad. It appears that the parent's viewDidLoad: gets called after the child's viewDidLoad:, which means the label is already created in the child's view.
override func viewDidLoad() {
super.viewDidLoad()
if let childVC = self.childViewControllers.first as? ChildVC {
childVC.someLabel.text = "I'm here. Aye-aye."
}
}
First of all you can't set directly EmbeddedVC's lable2.text In prepareForSegue
because call sequence following below
MainVC's prepareForSeque this time EmbeddedVC's label2 is nil
EmbeddedVC's viewDidLoad called then label2 loaded
MainVC's viewDidLoad called then label1 loaded
so if you assign MainVC's label1.text to EmbeddedVC's label2.text in prepareForSeque
both label1 and label2 are nil so did not work
There are two way to solve this question
First Solution
MainViewController has EmbeddedVC and when MainVC's viewDidLoad called, assign label1.text to embeddedVC.label2.text
class MyViewController: UIViewController {
#IBOutlet weak var label1: UILabel!
var embeddedVC: EmbeddedViewController? = nil
override func viewDidLoad() {
super.viewDidLoad()
label1.text = "Hello"
embeddedVC?.label2.text = label1.text
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let embeddedVC = segue.destination as? EmbeddedViewController {
self.embeddedVC = embeddedVC
}
}
}
class EmbeddedViewController: UIViewController {
#IBOutlet weak var label2: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
Second Solution, use protocol and get MainVC's label text when viewWillAppear or viewDidAppear (later viewDidLoad called)
protocol EmbeddedVCDelegate: class {
func labelText() -> String?
}
class MyViewController: UIViewController, EmbeddedVCDelegate {
#IBOutlet weak var label1: UILabel!
// MARK: EmbeddedVCDelegate
func labelText() -> String? {
return label1.text
}
override func viewDidLoad() {
super.viewDidLoad()
label1.text = "Hello"
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let embeddedVC = segue.destination as? EmbeddedViewController {
embeddedVC.delegate = self
}
}
}
class EmbeddedViewController: UIViewController {
#IBOutlet weak var label2: UILabel!
weak var delegate: EmbeddedVCDelegate? = nil
override func viewDidLoad() {
super.viewDidLoad()
}
override viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
label2.text = delegate?.labelText()
}
}
You should try to use prepareForSegue like this:
if segue.identifier == "identifier" {
guard let destinationViewController = segue.destination as? VC2 else { return }
destinationViewController.label2.text = mytext
}
Where the segue identifier you assign in storyboard

unexpectedly found nil while unwrapping an Optional value prepareForSegue

I am beginner in swift and working on one project where I am using collectionView. From collectionView, I want to transfer some values to details view but I am getting the above mentioned error. Values are not nil but somehow, it is giving this error while performing segue. Anybody help me, I am badly stuck here.
//In my CollectionView Controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "DetailsView")
{
if let vc:DetailsViewController = segue.destinationViewController as? DetailsViewController
{
vc.details.text = self.description
vc.line.text = self.subText
vc.startTime.text = self.formatted_time
}
}
}
//DetailsViewController
import UIKit
class DetailsViewController: UIViewController {
#IBOutlet var startTime: UILabel!
#IBOutlet var line: UILabel!
#IBOutlet var details: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
This basically means your IBOutlets are not yet initialised.
You should set strings and then in the viewDidLoad set you labels.
So to sum up:
Add string properties in your DetailsViewController
Set these string properties in your preparForsegue function
in the viewDidLoad of your DetailsViewController, set your labels
Your code should look like something like this :
//In my CollectionView Controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "DetailsView")
{
if let vc:DetailsViewController = segue.destinationViewController as? DetailsViewController
{
vc.detailsString = self.description
vc.lineString = self.subText
vc.startTimeString = self.formatted_time
}
}
}
//DetailsViewController
import UIKit
class DetailsViewController: UIViewController {
#IBOutlet var startTime: UILabel!
#IBOutlet var line: UILabel!
#IBOutlet var details: UILabel!
var startTimeString: String?
var lineString: String?
var detailsString: String?
override func viewDidLoad() {
super.viewDidLoad()
startTime.text = tmpStartTimeString
line.text = tmpLineString
details.text = tmpDetailsString
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Double check my code, I've wrote it very quickly ;)
The IBOutlets in a destination view controller aren't set until some time after prepareForSegue finishes
In DetailsViewController, add three instance variables:
var startTime: String?
var line: String?
var details: String?
Then in prepareForSegue, set those three values:
vc.details = self.description
vc.line = self.subText
vc.startTime = self.formatted_time
Then in viewDidLoad of DetailsViewController
detailsLabel.text = self.details
lineLabel.text = self.line
startTimeLabel.text = self.startTime
As the two answer above are point out when you create an instance of a UIViewController like for example in the prepareForSegue when you call segue.destinationViewController as? DetailsViewController this not mean that the #IBOutlet's are injected or initialized yet. The #IBOutlet's are initialized when the view is fully loaded, so you can do two of the following options:
Create variables in your UIViewController in which you can save the values after the init of the UIViewController in the prepareForSegue and then in the viewDidLoad() of the another UIViewController you set the values for the #IBOutlet's.
Another option is call the view (e.g let _ = vc.view) property when you create the instance of the UIViewController, in this way you can force the view to load load fully and you can set your #IBOutlet's from the prepareForSegue.
I hope this help you.

Delegate using Container View in Swift

I'm developing an app for iPad Pro. In this app, containerView use to add additional views and interact with them.
First, I created a protocol:
protocol DataViewDelegate {
func setTouch(touch: Bool)
}
Then, I created my first view controller
import UIKit
class ViewController: UIViewController, DataViewDelegate {
#IBOutlet var container: UIView!
#IBOutlet var labelText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func setTouch(touch: Bool) {
if touch == true {
labelText.text = "Touch!"
}
}
}
And finally, I created a view that will be embedded in containerView.
import UIKit
class ContainerViewController: UIViewController {
var dataViewDelegate: DataViewDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func touchMe(sender: AnyObject) {
dataViewDelegate?. setTouch(true)
}
}
But for some reason, nothing happened, the first view controller receives nothing in setTouch function.
My question is: In this case, using container, how can I make the communication between two ViewsControllers?
Like #nwales said you haven't yet set the delegate. You should do set the delegate in prepareForSegue function on your first viewController (who contain the viewContainer)
First select the embed segue and set an identifier in the attributes inspector.
Then in the parentViewController implement the func prepareForSegue like this:
Swift 4+:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "the identifier") {
let embedVC = segue.destination as! ViewController
embedVC.delegate = self
}
}
Below:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if (segue.identifier == "the identifier") {
let embedVC = segue.destinationViewController as! ContainerViewController
embedVC.dataViewDelegate = self
}
}
Looks like you defined the delegate, but have not set the delegate. This happens to me all the time.

How to send my score variable to another view controller?

After I click on a button I go to a new view controller.
I made it possible to make points with var score = 0 in a IBOutlet and to show to points I use Score.text = "\(++score)".
How can I pass the score to the 'Game over' screen/view controller to a label called "resultScore"?
Add an attribute secondViewController in the destination view controller, and use prepareForSegue
Current View Controller
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "segueTest") {
var svc = segue!.destinationViewController as secondViewController;
svc.toPass = textField.text
}
}
in secondViewController you can define one variable like String
var toPass:String!
In the secondViewController under the viewDidLoad function add this code
println(\(toPass))
One easy way to achieve that is you can use NSUserDefault for that.
first of all in your playScene when you are increasing your score you can store your score this way:
Score.text = "\(++score)"
NSUserDefaults.standardUserDefaults().setInteger(score, forKey: "userScore")
After that in your next view controller you can get score this way:
resultScore = NSUserDefaults.standardUserDefaults().integerForKey("userScore")
Remember that type of your resultScore should be Int.
FirstViewController
#IBOutlet weak var textField: UITextField!
...
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let vc = segue.destinationViewController as? GameOverViewController {
vc.score = Int(textField?.text)
}
}
...
SecondViewController
var score: Int!
#IBOutlet weak var label: UILabel!
...
override func viewDidLoad() {
super.viewDidLoad()
label.text = String(score)
}
This method will call when button was pressed, and in this place you may set some properties for your second view controller

Swift: Pass multiple values between views

I have a view that has two text fields and a button.
#IBOutlet var inputURL: UITextField!
#IBOutlet var inputName: UITextField!
#IBAction func submitUrlButton(sender: AnyObject) {
}
and a second view that has two variables:
var submittedURL = ""
var submittedName = ""
println("Name \(submittedName)")
println("URL \(submittedURL)")
In Swift How do I pass the values entered in the two text fields and assign them to those variables in the second view?
Thanks
EDIT FOR THETOM:
import UIKit
class ViewController: UIViewController {
#IBOutlet var inputURL: UITextField!
#IBAction func submitBtn(sender: AnyObject) {
performSegueWithIdentifier("submissionSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Create a new variable to store the instance of the next view controller
let destinationVC = segue.destinationViewController as BrandsViewController
destinationVC.submittedURL.text = inputURL.text
}
}
You can use the method prepareForSegue.
In the first view (the one from which the segue is coming from) write the following code :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Create a new variable to store the instance of the next view controller
let destinationVC = segue.destinationViewController as CustomViewController
destinationVC.submittedURL = inputURL.text
destinationVC.submittedName = inputName.text
}
Here CustomViewController is the custom class of the UIViewController to which the segue is going to.
To perform the segue programmatically in your button #IBAction do that :
#IBAction func buttonWasClicked(sender: AnyObject) {
performSegueWithIdentifier("submissionSegue", sender: self)
}
Since your view controllers are linked with segue you can override the prepareForSegue method in first view controller and pass data by doing so
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "secondViewController") { // here secondViewController is your segue identifier
var secondViewController = segue.destinationViewController as SecondViewController // where SecondViewController is the name of your second view controller class
secondViewController.submittedURL = inputURL.text
secondViewController.submittedName = inputName.text
}
}
And to performSegue inside your button action use perfromSegueWithIdentifier method
#IBAction func submitUrlButton(sender: AnyObject) {
//replace identifier with your identifier from storyboard
self.performSegueWithIdentifier("secondViewController", sender: self)
}
The simplest way of accessing values globally not neccessary to pass with segue
First View controller
import UIKit
var submittedURL:NSString? // declare them here
var submittedName:NSString? // Now these two variables are accessible globally
class YourViewController : UIViewController
{
#IBOutlet var inputURL: UITextField!
#IBOutlet var inputName: UITextField!
#IBAction func submitUrlButton(sender: AnyObject) {
if inputURL.text == "" && inputName.text == ""
{
//Show an alert here etc
}
else {
self.submittedURL.text = inputURL.text
self.submittedName.text = inputName.text
}
}
}
SecondView Controller
import UIKit
class SecondviewController: UIViewController
{
//inside viewDidload
println(submittedURL)
println(submittedName)
}