I have a TableView that when the user clicks it needs to show a detail view displaying the name of the row it clicked. I populate the tableView using a Json call but I can't figure out what I'm doing wrong.
This is my bits of code of ViewController
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var valueToPass:String!
#IBOutlet weak var tableView: UITableView!
// Instantiate animals class
var items = [animalObject]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
addDummyData()
}
func addDummyData() {
RestManager.sharedInstance.getRandomUser(onCompletion: {(json) in
if let results = json.array {
for entry in results {
self.items.append(animalObject(json: entry))
}
DispatchQueue.main.sync{
self.tableView.reloadData()
}
}
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier") as! CustomTableViewCell!
let animal = self.items[indexPath.row]
cell?.label.text = animal.name
return cell! //4.
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.row)!")
// Get Cell Label
// let indexPath = tableView.indexPathForSelectedRow!
var currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
print(currentCell.textLabel?.text)
valueToPass = currentCell.textLabel?.text
print(valueToPass)
performSegue(withIdentifier: "detailView", sender: indexPath)
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "detailView") {
// initialize new view controller and cast it as your view controller
let viewController = segue.destination as! DetailViewController
// your new view controller should have property that will store passed value
viewController.passedValue = valueToPass
}
}
}
and my detailView only has the following info
#IBOutlet weak var tooPass: UILabel!
var passedValue: String!
override func viewDidLoad() {
super.viewDidLoad()
print("DETAILS")
print(passedValue)
tooPass.text = passedValue
}
I'm not sure if the preparedToSegue is firing a little bit earlier because my terminal looks like this:
I used as a reference the following question any guidance will be appreciated
Try this for didSelectRowAt method.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
valueToPass = self.items[indexPath.row]
print(valueToPass)
performSegue(withIdentifier: "detailView", sender: indexPath)
}
Just pass the entire item in the items array.
Make the DetailViewController's passed item of type item. Then just access item.whateverPropertyRequired.
The problem is that in your cellForRow method, you are casting your cell to CustomTableViewCell type while in your didSelectRow, you are casting it as UITableViewCell. Change
var currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
in your didSelectRow to
var currentCell = tableView.cellForRow(at: indexPath)! as CustomTableViewCell
Good approach here is to use view model. Create model which contains your items and data associated with them. And at segue pass your data model. It’s bad coding, getting data right from cell on didSelect method, unless some ui working on that cell
Related
I'm having difficulty with passing a realm object's data from a tableViewController to a viewController. I want to choose one of the objects from the table and have that object's data be used in the following viewController. I've struggled to find resources here which deal with Realm objects instead of just passing strings and the likes. Any assistance would be great.
Here's my TableViewController with my attempt at creating a segue function to the second view :
class WorkoutController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return DBHelper.shared.getWorkout().count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
}
let muscle = DBHelper.shared.getWorkout()[indexPath.row]
cell.textLabel?.text = muscle
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let workoutDetailVC = segue.destination as! WorkoutDetail
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let name = DBHelper.shared.getWorkout()[indexPath.row]
workoutDetailVC.receivedName = name
}
}
}
Here's the WorkoutDetail class which is supposed to receive and show the data :
class WorkoutDetail: UIViewController {
#IBOutlet weak var muscleLabel: UILabel!
var receivedName = ""
override func viewDidLoad() {
super.viewDidLoad()
muscleLabel.text = receivedName
}
}
Here is the getWorkout() function referenced above :
func getWorkout()-> [String] {
var musclesName = [String]()
let storedExercise = realm.objects(Workout1.self)
for exercise in storedExercise {
musclesName.append(exercise.exercise)
}
return musclesName
}
I'm having real difficulty with this and would very much appreciate some assistance, thanks.
Get didSelectRowAt out of prepare then
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
}
let muscle = DBHelper.shared.getWorkout()[indexPath.row]
cell.textLabel?.text = muscle
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let name = DBHelper.shared.getWorkout()[indexPath.row]
self.performSegue(withIdentifier:"SegueName",sender:name)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let workoutDetailVC = segue.destination as! WorkoutDetail
workoutDetailVC.receivedName = sender as! String
}
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
I am making a simple client invoice list using a tableView with custom labels in each row. at the top you click the plus sign. This takes you to a second page where you type in your clients name. you hit save and it should add it to the label in the first view controller. I cannot get this to work.
my tableview storyboard has the file called ViewController.swift - in it
import UIKit
var invoiceList = [""]
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var clientTableView: UITableView!
#IBOutlet var clientLabel: UILabel!
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return invoiceList.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "clientItem", for: indexPath) as! UITableViewCell
let ClientName = invoiceList[indexPath.row]
cell.textLabel?.text = [clientInput]
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete
{
invoiceList.remove(at: indexPath.row)
clientTableView.reloadData()
}
}
override func viewDidAppear(_ animated: Bool) {
clientTableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
My second storyboard where you type in the text field. File is called AddInvoiceItem.swift. in it -
import UIKit
class AddInvoiceViewController: UIViewController {
var clientTextField:String!
#IBOutlet var clientInput: UITextField!
#IBAction func addItem(_ sender: Any) {
if clientInput.text != ""
{
invoiceList.append(clientInput.text!)
clientInput.text = ""
_ = navigationController?.popViewController(animated: true)
}
}
#IBAction func cancelBtn(_ sender: Any) {
_ = navigationController?.popViewController(animated: true)
}
}
My error keeps happening when I am trying to use the code line
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "clientItem", for: indexPath) as! UITableViewCell
let ClientName = invoiceList[indexPath.row]
cell.textLabel?.text = [clientInput]
return cell
}
The cell.textLabel?.text = [clientInput]
I know this is wrong, but cannot figure it out!
Make a new cell class
class CustomCell: UITableViewCell {
//Make your outlets here, connect the outlets from cell in your storyboard like ClientNameLabel
}
Please select the cell on your Storyboard, and enter the name of your class in this field. In your case it would be CustomCell
then in ViewController
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "clientItem", for: indexPath) as! CustomCell
let ClientName = invoiceList[indexPath.row]
cell.ClientNameLabel.text = ClientName
return cell
}
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
This question already has answers here:
Send data from TableView to DetailView Swift
(4 answers)
Closed 5 years ago.
I'm trying to recognize which tableviewcell is selected and after that i want to get the label value of that cell and pass it to the next view controller. My cell has a Label value which is string and a Number value which is Int. I'm using a firebase database to get all that data.
My code:
import UIKit
class PlacesTableViewController: UITableViewController {
//MARK: Properties
#IBOutlet weak var placesTableView: UITableView!
var places = [Places]()
override func viewDidLoad()
{
super.viewDidLoad()
// Loads data to cell.
loadData()
}
override func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
//return the number of rows
return places.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "PlacesTableViewCell"
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(places[indexPath.section])
self.performSegue(withIdentifier: "ShowCommentsTableViewController", sender: nil)
}
}
you can follow this thread to be useful send-data-from-tableview-to-detailview-swift
You have to create variable in destination view controller and store data in those Variable before Moving to Destination View controller.
This link will help you for this.
Try this:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let selectedPlace = places[indexPath.section]
self.performSegue(withIdentifier: "ShowCommentsTableViewController", sender: selectedPlace)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let selectedPlace = sender as? Places,
let destViewController = segue.destination as? SecondViewController {
destViewController.place = selectedPlace
}
}