Passing variable to second vc using didSelectItemAt in UICollectionView swift - swift

So what I've been trying to do this whole time is basically get a master-detail flow through three layers rather than two. That is, I'm trying to create a master (home) screen-> category screen-> detail (particular) screen flow in my app.
To do this, I'm trying to get a particular category to load depending on what is selected on the first screen. After that, I'll just load that particular item into the detail screen.
I tried set it up so that on didSelectItemAt the categoryToLoad variable is set, it then prepares the segue, passing a variable to the loaded vc, and shows. The opened vs should handle the key and open the category associated.
Ex. I click "Some Videos". categoryToLoad is set to "Some Videos". The segue begins preparing. It reads the categoryToLoad as "Some Videos", sets categoryKey to "Videos". It sets the categoryName of the second vc to "Videos". The second vc loads [Videos.plist].
However, with the current code, although it does this all correctly, the pressing of the button causes its own segue so that when I use performSegue it causes two segues, the segue of the storyboard as well as the performSegue.
When I remove performSegue(it does the single segue that only shows my blank default storyboard, loading as "Default"). If I go back and try again, it loads correctly the original pressed category.
Eg. I press Videos, am shown Default. I got back, press Resources, am shown Videos.
There's something wrong with the order in which things are being done, but I'm new so I'm having a hard time figuring it out. Been here looking for a while.
class HomeViewController: UICollectionViewController {
var categoryToLoad = ""
...
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "toCategorySegue"{
let catVC = segue.destination as! CategoryViewController
var categoryKey = "Default"
switch categoryToLoad {
case "Some Videos"
categoryKey = "Videos"
case "Help Resources"
categoryKey = "Resources"
...
default:
categoryKey = "Default"
catViewController.categoryName = categoryKey
}
override func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath){
categoryToLoad = photos[indexPath.item].caption
// Returns the title of a particular item (E.g. "Contact Us")
//alternatively, I also tried adding
//performSegue(withIdentifier("toCategorySegue", sender: self)) here
...
}
class CategoryViewController: UICollectionViewController{
var categoryName = ""
// here the vc loads the data for its collection view from
// a plist named [categoryKey].pList
...
}

Related

How to pass data between two screens while both are elements in NavigationController in Swift?

I have a set of storyboards under the UINavigation Controller. One of these screens contains a table view and I need to send the data of the clicked row to the next screen. I am aware that doing so would require me to use segue. But then how do I get the Navigation Bar and the Navigation Button
For better understanding I have uploaded the screen shot of my first screen. Clicking on the detail button is going to send the challenge object to next screen. However, I am looking forward to having the same navigation bar on the top of the next scree. Any idea how can I accomplish such objective?
Currently if I press on details button, I'll get the details of that challenge in the terminal.
#IBAction func CAStartChallenge(_ sender: UIButton) {
let tag = NSNumber(value: sender.tag) as! Int
print("challenge name to be printed : \(challengeTableConent[tag].display()) ")
}
In your storyboard you want to drag a segue between the view controllers, like so:
Storyboard segue dragging
Use either Show or Push (deprecated) options for the segue. Give this segue an appropriate identifier, such as Details.
Then in your button function you can add these:
#IBAction func CAStartChallenge(_ sender: UIButton) {
let tag = NSNumber(value: sender.tag) as! Int
performSegue(withIdentifier: "Details", sender: challengeTableConent[tag])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Details",
let destination = segue.destination as? DestinationViewController,
let model = sender as? ChallengeTableContentType {
// pass through model to destination view controller
destination.model = model
}
}
You can then configure the destination view controller using its specific model when it appears.
First of all
Passing data through screens using segues is simple. Just check this answer
Pass data through segue
It will explain exactly how to do it
Second
Check the bug on your button I think you mean "Details"
Third
Check the type of segue, you might not used push or show detail. Also select the details view and make sure that "Simulated Metrics -> Topbar" is set to inferred
Some prints to help
This will ensure that the navigation bar comes from the previous controller
This is how your segue should look, do not forget to give an identifier to your segue

Segue w/ Tab View Controller Keeps Passing Value

I am working on an iOS application that is built around a Tab View Controller. I have created a "Contacts" tab, where a user can find and select a contact from a list. When the user selects the contact, it takes the contact's name and passes it to a different tab. That function is being done like so:
func passName(name: String) {
let navTab = self.tabBarController!.viewControllers![2] as! UINavigationController
let homeTab = navTab.viewControllers[0] as! MainController
homeTab.passedName = name
tabBarController?.selectedIndex = 2
}
Everything works as it should so far (name is loaded into text field). My issue is that the value seems to keep coming back every time I change tabs and then go back to my Home tab. For example, if I select "John" from my contacts, it will take me to the Home Tab and put John's name in a textfield. Let's say I delete the last two letters of the name, so now it is "Jo". If I load a different tab and come back, the name field has been reset to "John". It's as if the value gets re-passed every time I open the Home Tab. Also, every time I load the Home Tab after passing a name, my console prints: "Name Passed: John", so it shows that this is being processed every single time the tab appears. Here is my code for processing the name:
var passedName: String!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//Checks if name was passed to controller
if let validName = passedName {
print("Name passed: \(validName)")
nameTextField.text = validName
}
}
Am I passing the data incorrectly? I was thinking it might be because I have the above code being called in the viewWillAppear method, but that doesn't make sense, as essentially the data is only being passed one time from the Contacts tab. Thanks!
The problem is that you're not actually passing the value back to the original view. Apple's recommendation for passing information between classes is to use the delegate pattern. This allows the modal view to call the delegate class's function, which changes the name local to the original view because that function is declared in the original view's viewController. You can read more about the pattern in this tutorial, but I've also included a brief example relevant to your use case below.
mainViewController:
class namesTableViewController: UITableViewController, editNameDetailsViewControllerDelegate {
var name : String
#IBAction func editButtonPressed(_ sender: UIBarButtonItem) {
performSegue(withIdentifier: "editPerson", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "editPerson" { //Modal segue
let navController = segue.destination as! UINavigationController
let controller = navController.topViewController as! editNameViewController
controller.delegate = self
if let person = sender as? Person {
print("Sending person to edit")
controller.personToEdit = person
}
} else {
super.prepare(for: segue, sender: sender)
}
}
//Protocol function
func changeName(n: String, controller: UIViewController) {
name = n
dismiss(animated: true, completion: nil)
}
}
editNameViewController:
class editNameViewController: UIViewController {
#IBOutlet weak var personNameTextField: UITextField!
var personToEdit : Person?
weak var delegate : PersonTableViewController?
override func viewDidLoad() {
super.viewDidLoad()
if personToEdit != nil {
personNameTextField.text = personToEdit?.name
}
}
// Button Actions
#IBAction func saveButtonPressed(_ sender: UIBarButtonItem) {
delegate?.personDetailsView(n: personNameTextField.text, controller: self)
}
}
Finally, the protocol class :
protocol editNameDetailsViewControllerDelegate : class {
func personDetailsView(n: String, controller: UIViewController)
}
Hope this helps.
The problem is "passedName" variable doesn't changed its value every time you edit it in your UITextField. Keep in mind that every time you change tabs, the UIViewController will call viewWillAppear and viewDidAppear. So your UITextField will always show passedName value once you select other tab and return.
I suggest that every time you edit the textfield you should update passedName value.
Sorry for my bad english.

Tableview passing same data to another Tableview

I have a an app that displays players through a Tableview. The user can "add" a new player and input his name, height, weight, picture, etc... and once saved will be added to the Tableview. They can also tap on each player and it takes them to the player detail view controller where it displays all of the information you entered (name, height, weight, throw, bat, position, etc...). This is the current code when a user taps on a player to take them to the player detail page:
var player2 = [Player]()
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "detail" {
let playerDetailScreen = segue.destinationViewController as! PlayerDetailViewController
if let selectedPlayerCell = sender as? PlayerListTableViewCell {
let indexPath = tableView.indexPathForCell(selectedPlayerCell)!
let selectedPlayer = player2[indexPath.row]
playerDetailScreen.playereDetail = selectedPlayer
}
}
}
I am trying to implement a tableview inside the view controller where the user adds a new player and enters the information. This table view is where a user can add the teams or years someone has played (I am using table view because the number of entries can vary). My question is what is the best way to go about this?? Right now I have a 2nd class named Teams.swift for this specific table view but is this the best way to do it? The idea is to then display this data in a tableview on the detail page (again, because the entries will vary in number) so how do I pass the data from one tableview to another??
Should I eliminate the [Teams] class and just put it under the [Player] class? Any thoughts would be appreciated!
Firstly, I would recommend using :
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// what happens when user selects a cell
let cell = self.tableView.cellForRowAtIndexPath.indexPath // This will give you cell that was selected
let selectedPlayer = cell.player // This gives you the instance of the player (Benefit of using player class)
self.performSegueWithIdentifier("identifier", sender : selectedPlayer)
// ^ This will automatically call prepareforseque method.
}
Next, inside prepareforsegue method, do this :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "detail" {
let playerDetailScreen = segue.destinationViewController as! PlayerDetailViewController
let selectedPlayer = sender as! Player
playerDetailScreen.name = self.selectedPlayer.name
playerDetailScreen.height = self.selectedPlayer.height
... so on.
// So, remember to create property for each name, height, etc.
// inside playerDetailScreen
// This is how you can transfer data
}
}
}

Data between view controllers not passing

I am creating an application in which there are 6 view controller in storyboard. The thing is that data is shared between the default view controller and the first one ( say A and B) which i added. i am using the prepareforseque method for passing data. the problem started when i added two more view controller. lets say C and D i created two new swift files and changed the two view controller class name. i created a textbox and button in C and label in D. when i pressed the button, the value of the text field is not passing into the D view controller although i used the same methods and code which i used for A and B. do i have to do anything else when i want to pass data between two newly added view controller.
first viewcontroller in which when a button is pressed value 1 needed to be passed:
class PlaySelectMenu: UIViewController {
var value = Int()
#IBAction func twotofive(sender: AnyObject) {
value = 1
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let nextView : PlayGameView = segue.destinationViewController as! PlayGameView
nextView.x = value
}
}
the second view controller which receive the value and print it
import Foundation
import UIKit
class PlayGameView: UIViewController{
var x = Int()
override func viewDidLoad() {
super.viewDidLoad()
print(x)
}
}
here i have added both the view controller from the object library and not working with the default one which is present in storyboard by default. i dont know why these two viewcontroller are not working. please help.
Regards Dev
One solution would be to write the data out to NSUserDefaults and then read it back from NSUserDefaults in the other view controller. Probably not the proper or correct way to share data between two view controllers, but it's been a reliable work around for me.
Other than that, you'd need to share your code so that we can see what's occurring.
Can you post also the code in your controllers C & D. And also if you have copy/paste the code inside your first two controllers into the two others, are you sure that in your prepareForSegue method you have changed the name of the destination segue ?
Assuming you have created the segue in Storyboard:
All you need is to do is put all of needed updates in prepareForSegue because twotofive is called after prepareForSegue.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
value = 1
let nextView : PlayGameView = segue.destinationViewController as! PlayGameView
nextView.x = value
}
Since you have connected your segue from button click to view controller, when you press button segue is automatically called. Instead of connecting segue from button to VC, connect VC to VC. Then in button click method at the last add below line:
#IBAction func twotofive(sender: AnyObject) {
value = 1
self.performSegueWithIdentifier("<Name of the segue identifier>", sender: self)
}
This will call your prepareForSegue. If you are calling more then one VC using segue from a VC then you can use segue.identifier to check which VC was called as below
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if segue.identifier == "CVC" {
}

Passing Core Data objects from UITableViewCell to another View Controller

Currently I have the application loading up the data in a tableview presented to the user that I want them to select the object they wish to view further details about. From their I want to be able to read out the data in the object to the user in various forms either labels or a tableview dependent on the data.
I have been reading around and trying to understand this stuff but it is all still new to me! I had another guy on here help me out with saving the data and passing the data around to store but reading it out is a seemingly different thing. Any examples in swift language would lead me down the right path I know that I need to use the didselectrow method and call a segue and also that I need a prepare for segue but I am not quite sure how it should look.
I have read multiple posts and some do actually attempt to pass objects but not in the manner in which I am trying to..Are you able to pass whole objects after they have been have been selected in a tableview to another view controller to present all data related to that object or are you only able to pass information from that object to the next viewcontroller? I have examples of prepareforsegue and perform segue before not sure what else I am missing but I am currently not able to pass any information between the tableview and the viewcontroller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "decktoRecord") {
var detailVC: StatWidgetViewController = segue.destinationViewController as! StatWidgetViewController
detailVC.deckRecord = decktoPass
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow()
let selectedCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell?
self.performSegueWithIdentifier("decktoRecord", sender: indexPath);
}
decktoPass is just a variable that contains the type of data entity that is the primary entity of the object.
A little confused by what you're asking but from what I understand, once the user clicks on a cell you want to segue to another view controller where they can edit the the details?
To add an exception breakpoint, open up your left panel with all your files/viewcontrollers, at the very top of it should be a small panel with a few icons, the first one is a folder, click on the second last one (the one that looks like a tag)
click on the plus in the bottom right and click add exception breakpoint, this should let you know where the problem in your code is occurring
Okay to edit the details in another View controller start by preparing the segue from the original view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showTaskDetail" {
let detailVC: TaskDetailViewController = segue.destinationViewController as! TaskDetailViewController
let indexPath = self.tableView.indexPathForSelectedRow()
let thisTask = fetchedResultsController.objectAtIndexPath(indexPath!) as! TaskModel
detailVC.detailTaskModel = thisTask
detailVC.delegate = self
}
else if segue.identifier == "showTaskAdd" {
let addTaskVC:AddTaskViewController = segue.destinationViewController as! AddTaskViewController
addTaskVC.delegate = self
}
}
okay so like the code is showing above, I have a segue called showTaskDetail which shows the details of whatever it is, in my case its a simple task. You said you want to edit this information in another view controller when a user clicks on the row, well you need to be able to get this information in the other view controller.
So create a variable in the other viewcontroller that will hold these values, for me i called it detailTaskModel
var detailTaskModel: TaskModel!
Incase you're confused about what TaskModel is, I'm using CoreData to store my data, and TaskModel is a NSMangedObject class.
let detailVC: TaskDetailViewController = segue.destinationViewController as! TaskDetailViewController
this line you're just specifying what your other view controller is, replace TaskDetailViewController with your swift class.
let detailVC: TaskDetailViewController = segue.destinationViewController as! TaskDetailViewController
this line fetches the data from the row selected
Now you should be able to pass your information into the other view controller and edit it using the detailTaskModel, do you need help with that as well?