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.
Related
I have a searchViewController where I search for users and UITableView gets updated dynamically with user information. The cell for the UITableView is custom - it has a UIImage, the usernameLabel, and a button called "Add".
What I want is that if the user clicks on the add button of the cell, it should pass the user information on that cell (image and username) to another view controller that has a UITableView that is a friend list.
However, so far the only way I know is by using performSegue to pass the data on to the other viewController holding the friendlist UITable. But by this method, every time I click the add button it segues to the other view controller which I don't want. I want it to stay on the searchViewController when the add button is clicked - I only want the data to be passed.
Is there any way I can do this? Is using NSUserDefaults advisable for passing data of this sort?
For simplicity I will use FriendListVC and AddVC
If you are going to your AddVC from FriendListVC via a bar button item or something and your stack looks like:-
FriendListVC -> AddVC
There are two approaches you can use:-
1) Create a delegate of your friendListVC in your addVC and modify the friendListVC datasource on any changes there
2) Or, and I recommend this approach, just reload your FriendListVC datasource on it's viewWillAppear. viewWillAppear will get called even if you navigate back. Thus even if you add a deleteVC in the future and navigate back, the viewWillAppear will perform the updates and it will be independent of any other VC
Hope that helps
Use delegate for passing data between view controllers. you can find this useful
Passing data between 2 UIViewController using delegate and protocol
you can use NSUserDefaults but delegate pattern is better than this.
You can use callback method best and easy way to pass data one controller to another
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
let viewControllerB = segue.destinationViewController as! ViewControllerB
viewControllerB.callback = { message in
//Do what you want in here!
}
}
In ViewControllerB:
var callback : (String -> Void)?
#IBAction func search(sender: AnyObject) {
callback?("Pass data to view controller1")
self.dismissViewControllerAnimated(true, completion: nil)
}
The easiest way to do this is by making an instance of the view controller that you want to pass data to, in the current view controller. I will write you a sample code for this.
class yourTableViewController: UITableViewController {
var controllerToPassData: UIViewController()
func clickTableButton(sender: UIButton) {
controllerToPassData.count += 1
}
}
class controllerwhereDataisPassed: UIViewController {
var count: Int!
}
Pick the instance of the controller where you want to pass data to from the navigationController stack and use this code.
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.
Let's say I have a firstViewController and a secondViewController. The first one contains a firstButton and the second one - a secondButton. Here's what I want to do: when user clicks the secondButton, some firstButton's property changes.
Unfortunately, when I create an instance of a firstViewController in a secondViewController and then trying to access a firstButton, I get an error:
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
So, technically, I'm trying to do this as follows:
var ins = firstViewController()
#IBAction func secondButtonisPressed(){
ins.firstButton.alpha = 0
}
What is the proper way to implement that?
Thanks in advance.
Your problem here is that the IBOutlets of your firstViewController are only available (!= nil) after the viewDidLoad() firstViewController's method has being called.
In other words, you have to present the view, before you can make any changes to a UIViewController IBOutlet.
How you can solve this?
Add a variable into FirstViewController that works as a flag for you.
for example: var hideFirstButton = false
in the viewDidLoad or viewWillAppear method of FirstViewController check for hideFirstButton's value and hide or show your firstButton.
Then, before you present your FirstViewController change the value of hideFirstButton to the needed for your application to run fine.
UPDATE:
Other workaround, using Storyboard is (This approach has the inconvenient that the completion handler is called after viewWillAppear() so the button is visible for a second):
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let firstViewController = storyboard.instantiateViewControllerWithIdentifier("FirstViewController") as! FirstViewController
self.presentViewController(firstViewController, animated: true, completion: {
//This lines will be called after the view is loaded so the code will run
firstViewController.firstButton.alpha = 0
})
EXAMPLE: an example at GitHub
You could try to do this using delegation, similar to the way Apple does it in their existing frameworks. For an example, look at the way that you use UITableViewDelegate when working with a UITableView object.
If you wanted to use delegation to tell secondViewController that firstButton was pressed using delegation, you could do it as follows:
Step 1:
Create a protocol containing a method for the button press event.
protocol buttonPressDelegate {
func buttonPressed() -> Void
}
Step 2:
In firstViewController, declare that you have an instance of an object of type buttonPressProtocol.
var buttonPressDelegateObj: buttonPressDelegate?
Step 3:
In firstViewController, initialize your buttonPressDelegateObj to contain a reference to your instance of secondViewController. If you want you can create a method to set the reference contained in buttonPressDelegateObj, or do it viewDidLoad in firstViewController, etc.
buttonPressDelegateObj = secondViewControllerObj
Step 4:
In secondViewController, declare that you adopt the buttonPressDelegate protocol.
class secondViewController: UIViewController, buttonPressDelegate {
Step 5:
In secondViewController, implement the protocol method buttonPressed() by adding the function with your desired implementation. Here's an example:
func buttonPressed() {
secondButton.alpha = 0
}
Step 6:
Create an #IBAction on the button in firstViewController, so that when the button is pressed it calls buttonPressDelegateObj.buttonPressed() and you can respond to the event
#IBAction func firstButtonPressed() {
if (buttonPressDelegateObj != nil) {
buttonPressDelegateObj.buttonPressed()
}
else {
print("You forgot to set your reference in buttonPressDelegateObj to contain an instance of secondViewController!")
}
}
Note: This is just one way that you could do this. To tell firstViewController that secondButton was pressed (go the other way), have firstViewController implement the protocol buttonPressDelegate, have secondViewController contain a reference to firstViewController as an instance of type buttonPressDelegate?, and create an #IBAction in secondViewController that fires when secondButton is pressed that calls your the buttonPressDelegate method.
Note: There is a similar pattern employed in the Android world to get a Fragment to communicate to an Activity, that you can read more about here
So I started working on a basic app and ran into an issue. The user is taken to a first view controller where they are asked to enter two team names and then press the start button (which performs a segue to the second view controller). On the second view controller are two labels (named homeIdentifier and awayIdentifier ) and I want the labels on the second view controller to update to the names the user put in on the first. I thought it would be simple, but have run into an issue; it says "fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)" It also says in red "Thread 1 EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP subcode=0x0)"
How do I do this? The start button is set to perform a segue to the second view controller already, so I don't know if something is going wrong with that process. I've tried the suggestions I've seen here but doesn't seem to work (I don't know if it's because I'm using swift 2, or because I'm transporting 2 variables, etc).
I don't think transporting two variables is the problem. Just make sure that you only perform the segue of there is two variables and one of them does not equal nil. Make sure to also create two variables in the second view controller as you will need to transfer the data over from your first view controller into those variables in the second view controller. To pass data in swift 2 I would use the prepare for segue method. You will also have to set and ID in the attributed inspector for the segue that takes you from the first view controller to the second view controller.
this goes in the first view controller .swift file
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "segueIdentifier") {
// make an instance of the second view controller
var detailVC = segue!.destinationViewController as DetailViewController;
detailVC.variable1 = "\(firstViewControllerVariable)"
detailVC.variable2 = "\(secondViewControllerVariable)"
}
}
this is what your second view controller .swift file should consist of
also make sure all your outlets are linked correctly
import UIKit
class secondViewController: UIViewController {
var variable1: String = String()
var variable2: String = String()
override func viewDidLoad() {
super.viewDidLoad()
homeIdentifier.text = variable1
awayIdentifier.text = variable2
}
}
hope this helps!
FirstVC
let sc=self.storyboard?.instantiateViewControllerWithIdentifier("SecondVC")as! SecondVC
sc.str1="Pass Text"
self.navigationController?.pushViewController(sc, animated:true)
SecondVC
class ViewDetails: UIViewController
{
var str1:String!
override func viewDidLoad()
{
super.viewDidLoad()
print(str1)
// Do any additional setup after loading the view.
}
}
I have two UICollectionViewControllers and the first one uses a push segue to get to the second one. The problem I'm having is passing information back to the first controller when the back button (the one that gets added automagically) is pressed in the second controller. I've tried using the segueForUnwindingToViewController, and canPerformUnwindSegueAction override functions, but no dice. I need to be able to access both view controllers so I can set some variables. Any ideas?
Here is an example with two view controllers. Let's say that the names of the two view controllers and ViewController and SecondViewController. Let's also say that there is an unwind segue from the SecondViewController to the ViewController. We will pass data from the SecondViewController to the ViewController. First, let's set the identifier of this segue by opening the document outline and selecting the unwind segue. Then open up the attributes inspector and set the identifier to "unwind".
SecondViewController Code:
class SecondViewController: UIViewController
{
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
if let destination = segue.destinationViewController as? ViewController {
if identifier == "unwind" {
destination.string = "We Just Passed Data"
}
}
}
}
}
ViewController Code:
class ViewController: UIViewController {
var string = "The String That Will Be We Just Passed Data"
#IBAction func unwindSegue(segue: UIStoryBoardSegue) {
}
}
It sounds like you are trying to intercept the back button, there are many posts for this on SO, here are two:
Setting action for back button in navigation controller
Trying to handle "back" navigation button action in iOS
In practice, it is more clear to return state in closures (more modern), or delegates.