How to send my score variable to another view controller? - swift

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

Related

Retrieving a variable from another view controller

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...

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.

Need to free memory after Modal Segues, but I need both my Segues to pass data from A to B and B to A

I am making a game for iOS with SpriteKit.
I have 2 Viewcontrollers. One is the GameViewController and the other one is the MenuViewController. Let's call them A and B respectively.
When the player dies, a function is called in GameScene.swift that launches a modal "Lost" Segue to B. There, the player can restart the game or buy a life and a "Back" Segue is called to A.
I need to dismiss the additional Views that get created each time I call a segue.
Problem is: I need the "Lost" Segue to send data about the Score to View B and I need the "Back" Segue to send data to View A about wether or not the player used a life.
I have implemented all this. But now I need to find how to dismiss old views that keep eating the device's memory, thus leading to lag and crash.
I have googled for hours and hours. No solution was adapted to my situation.
The solutions I found either caused my app to bug, data not to be passed or views not to be generated.
I will not add code here since there is a LOT. But I am sure the answer is actually really easy, just not for a beginner like me.
I think a possible solution would be an unwind segue from B to A ?
But do unwind segues pass data along ?
Moreover, I found no answer I could understand on how to use an unwind segue.
I exhausted all my possibilities. Stack Exchange is my last chance.
You definitely should use an unwind segue to return to the previous viewController, otherwise as you have found your memory usage increases until your apps quits.
I created the following example from your description. It uses a standard segue to move from the GameViewController to the MenuViewController and it uses an unwind segue to move from the MenuViewController back to the GameViewController.
The GameViewController has a Player Dies UIButton, a UITextField for entering a score, and a UILabel for displaying the lives.
The MenuViewController has a UILabel for showing the score, a Buy a Life UIButton for adding lives, and a Restart UIButton for returning to the GameViewController.
Here's the code:
GameViewController.swift
import UIKit
class GameViewController: UIViewController {
#IBOutlet weak var scoreTextField: UITextField!
#IBOutlet weak var livesLabel: UILabel!
var lives = 3
func updateLivesLabel() {
livesLabel.text = "Lives: \(lives)"
}
override func viewDidLoad() {
super.viewDidLoad()
updateLivesLabel()
}
// This is the function that the unwind segue returns to.
// You can call it anything you want, but it has to be in
// the viewController you are returning to, it must be tagged
// with #IBAction and it must take a UIStoryboardSegue as its
// only parameter.
#IBAction func returnFromMenu(segue: UIStoryboardSegue) {
print("We're back in GameViewController")
// Update the lives label based upon the value passed in
// prepareForSegue from the MenuViewController.
updateLivesLabel()
}
#IBAction func goPlayerDies(sender: UIButton) {
lives--
self.performSegueWithIdentifier("Lost", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Lost" {
let destinationVC = segue.destinationViewController as! MenuViewController
destinationVC.score = Int(scoreTextField.text ?? "") ?? 0
destinationVC.lives = lives
}
}
}
MenuViewController.swift
import UIKit
class MenuViewController: UIViewController {
#IBOutlet weak var scoreLabel: UILabel!
var score = 0
var lives = 0
override func viewDidLoad() {
super.viewDidLoad()
scoreLabel.text = "Score: \(score)"
}
#IBAction func buyLife(sender: UIButton) {
lives++
}
#IBAction func goRestart(sender: UIButton) {
self.performSegueWithIdentifier("Back", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Back" {
let destinationVC = segue.destinationViewController as! GameViewController
destinationVC.lives = lives
}
}
}
This is how you wire up the forward segue to be called programmatically:
Control-drag from ViewController icon to the MenuViewController:
Select Present Modally from the pop-up:
Click on the segue arrow between the viewControllers and give it an identifier in the Attributes Inspector:
This is how you wire up the unwind segue to be called programmatically:
Control-drag from ViewController icon to Exit icon:
Choose returnFromMenu from pop-up:
Click on the Unwind Segue in the Document Outline and give it the identifier "Back" in the Attributes Inspector on the right:
Alternate Answer
Instead of using segues, you can present and dismiss viewControllers manually. The advantage for your app is that the MenuViewController will be allocated only once and will persist for the life of the app. This same viewController will be presented and dismissed repeatedly, but it will not be deallocated which I suspect is leading to your crashes.
The GameViewController will be the initialViewController that is created by the Storyboard. The MenuViewController will be loaded in viewDidLoad of the GameViewController.
To make this work, you need to add an identifier to the MenuViewController so that it can be instantiated by name. Click on the MenuViewController in the Storyboard and set its Storyboard ID in the Identity Inspector:
Here is the code. Note that all mention of segues is gone. Note how viewWillAppear is used to update the viewControllers.
GameViewController.swift
import UIKit
class GameViewController: UIViewController {
#IBOutlet weak var scoreTextField: UITextField!
#IBOutlet weak var livesLabel: UILabel!
var menuViewController: MenuViewController?
var lives = 3
func updateLivesLabel() {
livesLabel.text = "Lives: \(lives)"
}
override func viewDidLoad() {
super.viewDidLoad()
menuViewController = self.storyboard!.instantiateViewControllerWithIdentifier("MenuViewController") as? MenuViewController
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
updateLivesLabel()
}
#IBAction func goPlayerDies(sender: UIButton) {
lives--
menuViewController?.score = Int(scoreTextField.text ?? "") ?? 0
menuViewController?.lives = lives
self.presentViewController(menuViewController!, animated: true, completion: nil)
}
}
MenuViewController.swift
import UIKit
class MenuViewController: UIViewController {
#IBOutlet weak var scoreLabel: UILabel!
var score = 0
var lives = 0
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
scoreLabel.text = "Score: \(score)"
}
#IBAction func buyLife(sender: UIButton) {
lives++
}
#IBAction func goRestart(sender: UIButton) {
let destinationVC = self.presentingViewController as! GameViewController
destinationVC.lives = lives
self.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
}
}

Why can't I assign value on didSet?

I have two view. On my first view i create some data and send to other view lets name view2. So my view1 has code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showUserDetail" {
(segue.destinationViewController as! UserDetailTableViewController).detailContact = detailCoversationContact
}
}
In my detailCoversationContact i have detail about this one user, this detail like number, nick etc. Now as i expect this data are put to variable detailContact in my view2
And this is my cod in view2 this code is work:
class UserDetailTableViewController: UITableViewController {
#IBOutlet weak var nickNameLabel: UILabel!
var nickMy: String = ""
var detailContact: AnyObject? {
didSet {
// Update the user interface for the detail item.
if let detail = self.detailContact {
self.nickMy = (detail.valueForKey("nickName") as? String)!
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
nickNameLabel.text = nickMy
}
}
When i send data to detailContact' i check is didSet if yes i set my nickMy and this is work. But what is first ? setting my var or viewDidLoad?
this is my code and not working and can someone explain why ?
class UserDetailTableViewController: UITableViewController {
#IBOutlet weak var nickNameLabel: UILabel!
var detailContact: AnyObject? {
didSet {
// Update the user interface for the detail item.
if let detail = self.detailContact {
self.title = detail.valueForKey("nickName") as? String
self.nickNameLabel?.text = (detail.valueForKey("nickName") as? String)!
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Ok this is self.title and work i see the name of user but title is from tableView. But my nickNameLabel is not change ? why ?
At the time of prepareForSegue, the IBOutlets have not been set up yet. So, self.nickNameLabel is still nil, and optional chaining makes self.nickNameLabel?.text = ... safely do nothing.
Set the value in viewDidLoad() when the IBOutlets have been set up:
override func viewDidLoad() {
super.viewDidLoad()
self.nickNameLabel?.text = (detail.valueForKey("nickName") as? String)!
}
Alternatively, you can trigger didSet by setting the detailContact to itself in viewDidLoad(). You have to trick Swift in order to do this because it thinks assigning detailContact to itself does nothing:
override func viewDidLoad() {
super.viewDidLoad()
detailContact = (detailContact) // trigger didSet to set up the label
}

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)
}