I have a tableviewcontroller named DetailTableViewController and I need to pass data to next tableviewcontroller named DetailTableViewController. But I need to pass the data as the user clicks the cell of DetailTableViewController. Then, I got error as "unexpectedly found nil while unwrapping an Optional value".Please help me sort out in Swift 3.
First of all, never get data from the view (the cell) in such a case, get it always from the model (data source array).
Second of all, the signature of prepare(for segue is wrong for Swift 3.
In didSelectRowAt pass the indexPath rather than self, you can also delete valueToPass.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "Cell", sender: indexPath)
}
In prepareForSegue get the data from the model and pass it to the detail view controller:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Cell" {
let viewController = segue.destination as! GuidesDetailsTableViewController
let indexPath = sender as! IndexPath
viewController.passedValue = guides[indexPath.row]
}
}
First create a variable in your detail view controller to hold the value which you send from first view controller. then assign the value inside the didSelectRowAt method in first view controller and then navigate programatically. in this way, you do not need to implement prepare for segue method to send the data.
you just need to implement the didselectrowat, method.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selected = firstArray[indexPath.row]
let detailvc = self.storyboard?.instantiateViewController(withIdentifier: "DetailTableViewController") as! DetailTableViewController
detailvc.type = selected
self.present(detailvc, animated: true, completion: nil)
}
I created a video tutorial for you from scratch. how to pass data from one table view controller to another. hope this will help to you.
Inside your didSelectRow you're setting a new indexPath. Removing it should do the trick.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Don't reassign indexPath
// let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
valueToPass = currentCell.textLabel?.text
performSegue(withIdentifier: "Cell", sender: self)
}
It seems that the var valueToPass don't have a value when you call the performSegue. Try to assign the value of valueToPass with the following code:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
valueToPass = guides[indexPath.row]
performSegue(withIdentifier: "Cell", sender: self)
}
And create your segue form your viewController, maybe you create the segue from the cell.
Related
Im a absolute beginner and my English isn't well .
I would like to create a program for a person who wants to learn German.
He can pick up a topic from a picker view and then display the translation in a table view with audio and a picture.
My problem right now is that I can send the topic(SegueStatus1) from the Picker view to my table view( which contains all the arrays), but I can not identify the array with the variable that I had before. I have now manually selected the Array(Aile).
The TABLEVIEW
import UIKit
// Two Arrays which can be chosen
var Aile = ["Anne","Baba","Dede"]
var ABC = ["A","B","C"]
var myIndex = 0
class TableEins: UITableViewController {
override func viewDidLoad() {super.viewDidLoad()}
//Two Strings from View before
var SegueStatus1: String?
var SegueStatus2: String?
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{return Aile.count}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = Aile[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
myIndex = indexPath.row
performSegue(withIdentifier: "AuswendigLernenSegue2", sender: self)
}
}
I am thankful for every help
EDIT :
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if (segue.identifier == "AuswendigLernenSegue")
{
let Status1: TableEins = segue.destination as! TableEins
Status1.SegueStatus1 = KonuT.text
}
I have a pickerView and a tableView both are working.
In the next step I want the pickerView and the tableView being connected.
The variable that determines which array I want to display is SeagueStatus1.
I want to use a solution that gets my variable right where the name of the array is needed.
Hope this helps:
class TableEins: UITableViewController {
// Avoid using magic strings.
let cellIdentifier = "cell"
let segueIdentifier = "AuswendigLernenSegue2"
// Always use lowercases for your variable and make them `let` unless you're going to manipulate them later. This tells everyone it's set only once and never modified.
// Also avoid global variables, I nested these inside the TableEins.
// Use snake case (ex: someVariable) for all variables. This lets someone identify the variable easily from classes and makes your code less confusing.
let aile = ["Anne","Baba","Dede"]
let abc = ["A","B","C"]
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return aile.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
cell.textLabel?.text = aile[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// This will always perform this segue no matter what cell you click on.
// If you want to perform this segue on only some of the cells, you need to check the index paths
performSegue(withIdentifier: segueIdentifier, sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
// Guard is a great way to prevent from having too many nested if statetments and knowing your method execution stops here.
guard segue.identifier == segueIdentifier else { return }
// Get the index path from the sender
guard let indexPath = sender as? IndexPath else { return }
// Get your data using the index path and pass it to your view controller we are navigating to
// You were loading the TableEins as the destination view controller. Thats this class. You may have made a mistake here.
}
}
How to make Each Cell open the specific view for its indexpath.
In tableview didselect what should i do to make each cell open as its own indexpath so each cell have a different data in the next view
i am tryin when click in a cell in the tableview the next view present it self with it's own data as the next view contain a uitextview like in note app
what should i apply at row didselect
// MARK: -TableFunctions
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return SavingTasks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let newtask = self.SavingTasks[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.TheLabel?.text = newtask
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if(editingStyle == .delete)
{
self.SavingTasks.remove(at: indexPath.row)
self.TasksTable.deleteRows(at: [indexPath], with: .fade)
GetData()
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let NewIndex = self.SavingTasks[indexPath.row]
let view = self.storyboard?.instantiateViewController(withIdentifier: "TaskDetail") as! TaskDetail
view.SavingDetails = [NewIndex]
view.Index = indexPath.row
self.navigationController?.pushViewController(view, animated: true)
}
next class should be appeared
class TaskDetail: UIViewController {
var Delegate: NoteDetailDelegate!
var SavingDetails = [String]()
var Index: Int?
#IBOutlet weak var TaskDetailsFiled: UITextView!
#IBAction func SaveTDF(_ sender: UIButton) {
UserDefaults.standard.set(TaskDetailsFiled.text, forKey: "Saved")
self.navigationController?.popViewController(animated: true)
}
You can use a segue and prepare(for:sender:) to get the next view controller ready more easily than instantiating the view controller and popping it via code. Official documentation here and a sample app from Apple here
An implementation example:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "mysegue"{
if let nextViewController = segue.destination as? NextViewController{
nextViewController.Index = 2
}
}
}
A highlight from the official doc:
For example, if the segue originated from a table view, the sender parameter would identify the table view cell that the user tapped
If you want to stick with the code implementation, you can call view.myvariable = myvalue in your didSelect
there is a class MenuViewController in which the array recorded in the table is recorded:
import Foundation
import UIKit
class MenuViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var menuTableView: UITableView!
let myTitle = ["Помощь", "Информация", "Поддержка"]
override func viewDidLoad() {
super.viewDidLoad()
menuTableView.delegate = self
menuTableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myTitle.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = menuTableView.dequeueReusableCell(withIdentifier: "MenuCell") as! MenuTableViewCell
cell.labelText.text = myTitle[indexPath.row]
return cell
}
}
What do you need to write in it to go to Info Controller?
I assume you are using storyboards. First you need to set up some sort of connection between your two viewcontrollers. You can do this by setting up a "segue". You hold ctrl+click drag from the first viewcontroller to the 2nd viewcontroller. It looks like this:
When you let go of the mouse, you will see this:
Click on Show. Now you will have created a segue.
Now you need to name it. So click on the segue that shows up in the storyboard. It is basically just an arrow from the Menu View Controller to the Info View Controller. On the right hand side you will see a place where you can name it (give it an identifier):
Now ... you have done all you needed to do in the storyboard. Now in your actual code in the MenuViewController, you need to associate clicking the tableviewcell. You do this by using the delegate method didSelectRowAt.
I see you have already set up the delegate with this line:
menuTableView.delegate = self
That ensures that didSelectRowAt will be called whenever the user taps on a row.
So now what you want to do is write the code to perform the segue:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "YourSegueName", sender: self)
}
Note:
Here is more information on interacting with a tableview row:
How to detect tableView cell touched or clicked in swift
Here is more information about segues:
IOS - How to segue programmatically using swift
There are many more customizations you can do ... such as passing data through to the next viewcontroller via the segues. Here's how:
Pass data through segue
implement UITableViewDelegate method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//To InfoViewController I am considering you have created InfoViewController with XIB
let controller = InfoViewController(nibName: "YourNibName", bundle: nil)
//Present Your Controller
self.present(controller, animated: true) {
}
//Push Your controller if your view is already part of NavigationController stack
self.navigationController?.pushViewController(controller, animated: true)
}
Just add a
nextViewController.didMove(toParentViewController: self) inside a
"didSelectRowAt". Example given below.
This is for List a tableview cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "cell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! PatientFamilyDetailTableViewCell
cell.txt_patientName.text = patientname[indexPath.row]
// Configure the cell...
return cell
}
Now it's for Select a Table view cell:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
if cell.isSelected {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "medtestmain") as! medTest
patient_id = relation[indexPath.row]
patient_name = patientname[indexPath.row]
UserDefaults.standard.set(patient_id, forKey: "patient_id")
UserDefaults.standard.set(patient_name, forKey: "patient_name")
self.addChildViewController(nextViewController)
nextViewController.view.frame = self.view.frame
self.view.addSubview(nextViewController.view)
nextViewController.didMove(toParentViewController: self)
}
}
}
I'm trying to pass a value from one view controller to another. As i tested this is ok and runs with my code. But i have a problem when i'm trying to take cell label value. I tried everything i found on google and stackoverflow, but there is no answer on this. I added a comment on code which says // I THINK THE PROBLEM IS ON THE NEXT LINE OF CODE to help you find where i'm stuck.
My code:
import UIKit
class PlacesTableViewController: UITableViewController {
#IBOutlet weak var placesTableView: UITableView!
var places = [Places]()
var valueToPass:String!
let cellIdentifier = "PlacesTableViewCell"
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
// Table view cells are reused and should be dequeued using a cell identifier.
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? PlacesTableViewCell else {
fatalError("The dequeued cell is not an instance of PlacesTableView Cell.")
}
let place = places[indexPath.row]
cell.placeLabel.text = place.name
cell.ratingControl.rating = place.rating
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
print("You selected cell #\(indexPath.item)!")
let indexPath = placesTableView.indexPathForSelectedRow
// I THINK THE PROBLEM IS ON THE NEXT LINE OF CODE
let currentCell = placesTableView.cellForRow(at: indexPath!)! as UITableViewCell
print(currentCell.detailTextLabel?.text ?? "Failed to use Cell Label")
valueToPass = currentCell.detailTextLabel?.text ?? "Failed to use Cell Label"
performSegue(withIdentifier: "ShowCommentsTableViewController", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "ShowCommentsTableViewController") {
// initialize new view controller and cast it as your view controller
let viewController = segue.destination as! CommentsTableViewController
// will be stored in passedValue on the CommentsTableViewController
viewController.passedValue = valueToPass
//print (viewController.passedValue ?? "")
}
}
}
Why don't you just use the raw value from the data store like you do in the cellForRowAt?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let place = places[indexPath.row]
valueToPass = place.name
performSegue(withIdentifier: "ShowCommentsTableViewController", sender: self)
}
Replace your didselect method with the given below
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let place = places[indexPath.row]
valueToPass = place.name
performSegue(withIdentifier: "ShowCommentsTableViewController", sender: self)
}
There are two basic issues in your current code, first you are casting your cell into UITableViewCell instead of PlacesTableViewCell and the second issue is that you are accessing detailTextLabel which is not available in your custom cell instead of using placeLabel. The correct code will be like that:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
print("You selected cell #\(indexPath.item)!")
let indexPath = placesTableView.indexPathForSelectedRow
// I THINK THE PROBLEM IS ON THE NEXT LINE OF CODE
let currentCell = placesTableView.cellForRow(at: indexPath!)! as PlacesTableViewCell
print(currentCell. placeLabel?.text ?? "Failed to use Cell Label")
valueToPass = currentCell. placeLabel?.text ?? "Failed to use Cell Label"
performSegue(withIdentifier: "ShowCommentsTableViewController", sender: self)
}
And if you dont want to go through this hassle , you can simply get the values from the original array like that:
let place = places[indexPath.row]
valueToPass = place.name
I am trying to pass cell's tag data to another viewcontroller's variable
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let currentCell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell!
print(currentCell.tag)
valueToPass = currentCell.tag
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let Dest = segue.destinationViewController as! EventDetail
if (segue.identifier == "eDetay") {
Dest.eventid = String(valueToPass)
}
}
When i try as it is, valueToPass is always passed the previous data.
Is there a way to get selected cell data from prepareForSegue ?
What is the best way to do that?
You are overriding the indexPath parameter that is being passed to you.
Remove the first let statement. Use the indexPath that is being passed to you.
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!)
{
let cell = tableView.cellForRowAtIndexPath(indexPath) as YourCell
//cell.value, cell.text, cell.randomValue or whatever you need
}