Variable not storing? - ios

I am currently developing a tactical screen app where one can access a database to add to add players into their screens. The database is working fine, I am now trying to pass over player information into the selected position. The player information passes over fine, but I am now having trouble with trying to implement that player information into the selected position:
var selectedP: Int?
#IBAction func selectAPlayer(_ sender: UITapGestureRecognizer) {
self.selectedP = sender.view!.tag
//print (selectedP!)
}
Above is the method which demonstrates how I am trying to save the selected position's tag with selectedP, so I can access its subviews. The correct tag prints out in the above method. However, when I try to call it in another method, the variable returned is always nil. I'm not exactly sure what the problem is. Here is the method where I try to call the selectedP variable:
func setPlayer () {
//print(selectedP!)
}
Simply printing selectedP crashes the program as it is obviously equivalent to nil. Is there anything I am doing wrong?
I must note that the setPlayer() method is called by a segue from another class which is essentially a View Player class. This is shown as a popover in the application. I'm not sure that if you call a popoverController the variables essentially get restored?

Correct me if I'm wrong, but I believe you set your variable as self.selectedP, not selectedP. I'm not familiar with swift, but this concept is fairly universal. In python:
class foo():
def setBar():
self.bar = True
print(str(self.bar)) #prints True
print(str(bar)) #throws error

Figured it out. Had to pass over the variable to the popover, and then back. Here's how I did it in a more generic way:
let viewController = "addPopover"
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: viewController) as? PopoverViewController
// Above we get the popover, below we set a variable in that popover's class.
vc?.varThatNeedsToBeStored = sender.view!.tag
Then in my prepare segue method in the popover class:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let nextScene = segue.destination as? TacticalCentreViewController
nextScene?.varThatNeedsToBeStored = varThatNeedsToBeStored
}
This now returns the correct tag value.

Related

Swift 3: only nil values in destination view controller of segue [duplicate]

This question already has answers here:
Changing label text of second ViewController upon clicking button in first ViewController
(4 answers)
Closed 6 years ago.
When using the prepare (forSegue) method to pass data to the destination view controller of a segue, all members of the destination VC are nil. Thus, my program crashes when trying to access these members.
My code in the source view controller:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Second2DetailEdit"{
var detailVC = segue.destination as! DrinkDetailViewController
detailVC.headerLabel.text = "Edit Drink"
}
//...
}
The VC itself is not nil. Only all members of the VC. Did I forget something?
This does only occur in my new Swift 3 project. Similar code in my Swift 2 projects have no problems.
Thanks in advance.
The destination view controller's view is not created yet and that's why all IBOutlets are nil. Try sending the information you need in normal properties like Strings and in ViewDidLoad of the destination, update your view.
The best way around this is to create a variable in your second view controller and set the text to that instead.
Then once the vc has initialized you can set the headerLabel.text to the new variable in viewDidLoad
I would suggest safely unwrapping your detailVC rather than force unwrapping...
if segue.identifier == "Second2DetailEdit" {
if let detailVC = segue.destination as? DrinkDetailViewController {
detailVC.headerLabel.text = "Edit Drink"
}
}
You'll have an easier time setting properties on the destinationVC and passing info to those rather than trying to do it through UILabels...

How can I pass data to a viewcontroller and load it before pushing it onto Navigation stack?

I have a view controller with a collection view and when I click a cell, I pass a variable to another view controller, then push it onto the navigation stack.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("nextVC", sender: self.userArray[indexPath.row])
}
This is how I send the variable:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
super.prepareForSegue(segue, sender: sender)
let nextVC = segue.destinationViewController as! NextVC
if let senderId = sender!.objectForKey("uId") {
nextVC.userId = senderId as! String
}
}
This next view controller (nextVC) has a lot of stuff on it. I have done everything I can to optimize it but it still takes some time to load all the stuff. Is there a way I can prepare for segue, pass the variable, and let the next vc do it's thing, then when it's done loading, actually segue to it? (or show it)? I saw some other questions which talk about preloading view controllers but none of them dealt with passing a variable. This variable is very important because all the stuff in the nextVC has to know this one variable (based on cell touched) or it won't be able to load. I hope I explained this well enough, thanks for your time.
If you access the view property of the destinationViewController, it will trigger the view to load. Since you don't really need the view, just assign it to _ to keep the compiler happy.
Try this:
if let senderId = sender!.objectForKey("uId") {
nextVC.userId = senderId as! String
_ = nextVC.view
}
This will trigger viewDidLoad to run before the segue happens. Make sure your code to set up your NextVC is in viewDidLoad and you should be good to go.

Segue to UIViewController without initializing a new object

I am building in iOS 9 with Swift 2.0. I have my starting UIViewController that is my menu screen. It contains the following code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let id = segue.identifier where id == "GamePlayScene" {
self.gameVC = segue.destinationViewController as? GameViewController
self.gameVC!.delegate = self
if let s = sender as? GKTurnBasedMatch {
self.gameVC!.match = s
}
}
}
When segueing to my GameViewController, the following init runs before that prepareForSegue even gets called:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
GKLocalPlayer.localPlayer().registerListener(self) // I only want this once
}
In storyboard, my GameViewController has a "Menu" button that is connected to the exit widget for the view controller and it unwinds to the Menu as intended. But whenever I perform the segue again, the init gets called again so I now have multiple GameViewControllers. I think this slows my app down since I am using SKScenes. How do I perform a segue without it creating a new object every time?
func player(player: GKPlayer, receivedTurnEventForMatch match: GKTurnBasedMatch, didBecomeActive: Bool) {
if didBecomeActive {
// This event is what activated the app, so the user wants it right meow
GameKitHelper.sharedInstance.match = match
performSegueWithIdentifier("GamePlayScene", sender: match)
}
}
You can store your GameViewController in a singleton so that it only needs to be created once. Since the view controller is initialized only once it saves processing time. This is probably a good performance optimization for an app using two view controllers, such as your game, that need to frequently be switched back and forth between the two.
The way to do this is to create a new Swift class and have it accessible as a shared instance where the second view controller is stored in a private property. The second view controller is instantiated if it has not yet been initialized. Further retrievals of the view controller return the stored view controller thereby eliminating the need to initialize the view controller. The operations for creating the view controller, storing it, and returning it are handled by the getter of a publically accessible computed property.
Here is the code for the class:
Singleton.swift:
import Foundation
import UIKit
class Singleton {
static let sharedInstance = Singleton()
var gameViewController: GameViewController {
get {
if self.storedViewController == nil {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
self.storedViewController =
storyboard.instantiateViewControllerWithIdentifier("GameViewController") as? GameViewController
}
return self.storedViewController!
}
}
private var storedViewController: GameViewController?
}
To use this, it will be necessary to use the showViewController method of showing the game view controller instead of using a segue.
In the first view controller I have:
#IBAction func buttonWasPressed(sender: AnyObject) {
let vc = Singleton.sharedInstance.gameViewController
navigationController?.showViewController(vc, sender: self)
}
The second controller (GameViewController) will now only be created once and re-used every time the button is pressed on the first view controller.
GKLocalPlayer.localPlayer().registerListener(self) // I only want this once
The way to cause a line of code to run only once over the lifetime of the app is with dispatch_once.

Update a variable in a TableViewController

I need to update a variable in a TableViewController and I can't find the way to do it, can someone explain me why this is not working please?
I'm getting mad.
From my view controller this is the code I'm running:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let TV = storyboard.instantiateViewControllerWithIdentifier("tbController") as myTableViewController
TV.x = "test"
And then, from the TableViewController class:
class myTableViewController: UITableViewController {
var x:String!
override func viewDidLoad() {
super.viewDidLoad()
println("Value of x is: \(self.x)")
}
}
And the printed value is: nil
Why? What is wrong with that? I don't understand :-(
Updated Picture
First, give the segue between the ViewController and the TableViewController an identifier (Example: "TableViewSegue"
Then in the ViewController, use prepareForSegue to pass data from ViewController to TableViewController
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 == "TableViewSegue" {
let vc = segue.destinationViewController as myTableViewController
vc.x = "Test"
}
}
There could be several issues here.
1. You should embed the tableview into the initial viewcontroller and create it as an IBOutlet
Edit: from the updated picture it appears that you want to click the top right button and go to the tableview. Therefore, this is an incorrect statement.
You also need to make your Viewcontroller (either the tableviewcontroller or the main viewcontroller if you chose to follow #1 above) a UITableViewDelegate and UITableViewDataSource
If you are expecting to change the label listed on the image shown, you will need to use label.text = "String" assignment to change what is displayed there
You have not set an initial variable for x inside the tableviewcontroller.
Also, as a point, your order of operations isn't properly set, so it will always display nil. Because if you look at how you built this:
You have a println inside of a viewdidload on the tableview. This variable you are printing has NOT been set yet, so it is nil
You then created an instance of this class. As soon as you created that instance, the viewdidload method fired and it printed a nil line.
THEN you changed the variable via the TV.x method. But there is no println check there so you're not able to see what you did.

Modifing one variable from another view controller swift

I am developing an app in Swift that, in gist, tells people the price of Bitcoin in various currencies. To select the currency, the user chooses from a list in a view controller with a UITableView. This is currencyViewController, and it is presented from my main screen, viewController.
What I want to happen is that, when the user dismisses currencyViewController, it passes a string to a UIButton in the main viewController.
Here's the prepareForSegue function that should pass the data:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "presentCurrency") {
currencySelector.setTitle("\currencySelected", forState: UIControlState.Normal)
}
}
CurrencySelector is a UIButton in the main viewController, and currencySelected is a variable in the second view controller, currencyViewController.
It gives the error "Invalid Escape Sequence In Literal"
So, I've narrowed it down to one of two issues:
The variables from viewController can't be "seen" from currencyViewController. If so, how can I modify the text of CurrencySelector from CurrencyViewController?
For some reason, when the user exits the pushed CurrencyViewControler, prepareForSegue isn't called.
What is going on here? Thanks, and apologies - I am but a swift newbie.
2 - "prepareForSegue" is called when you push a new view controller via the segue, but not when you dismiss it. No segue is called upon dismissal.
1 - A good way to do this would be the delegate pattern.
So the main view controller would be the delegate for the currencyViewController, and would receive a message when that controller is dismissed.
In the start of the currencyViewController file you prepare the delegate:
protocol CurrencyViewControllerDelegate {
func currencyViewControllerDidSelect(value: String)
}
and you add a variable to the currencyViewController:
var delegate : CurrencyViewControllerDelegate?
Now, the mainViewController has to conform to that protocol and answer to that function:
class MainViewController : UIViewController, CurrencyViewControllerDelegate {
//...
func currencyViewControllerDidSelect(value: String) {
//do your stuff here
}
}
And everything is prepared. Last steps, in prepareForSegue (MainViewController), you will set the delegate of the currencyViewController:
var currencyVC = segue.destinationViewController as CurrencyViewController
currencyVC.delegate = self;
And when the user selects the value in currencyViewController, just call that function in the delegate:
self.delegate?.currencyViewControllerDidSelect("stuff")
A bit complex maybe, but it's a very useful pattern :) here is a nice tutorial with more info if you want it:
http://www.raywenderlich.com/75289/swift-tutorial-part-3-tuples-protocols-delegates-table-views
You have to use the parantheses to eval variables in strings, i.e. println("\(currencySelected)")
To access variables in the second view controller (the one which is the destination of the segue) you have to get a reference to it:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "presentCurrency") {
let currencyViewController = segue.destinationViewController as CurrencyViewController // the name or your class here
currencySelector.setTitle("\(currencyViewController.currencySelected)", forState: UIControlState.Normal)
}
}

Resources