I want to pass the Label from a TableViewCell to a ViewController when I click on the Cell. In the end it should be like twitter, that if you click on a cell with a Label you get passed on a detailViewController with the same Label init.
My Code doesn't work as I just get the blanket Label from the Storyboard...
import UIKit
import Firebase
class JobTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var valueToPass:String!
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
// Arvice return to count jobs
return jobs.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
let valueToPass = currentCell.textLabel?.text
print("value: \(valueToPass)")
performSegue(withIdentifier: "toDetails", sender: valueToPass)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "JobCell", for: indexPath) as! JobTableViewCell
let job = jobs[indexPath.row]
cell.job = job
//spacing
cell.contentView.backgroundColor = UIColor.clear
let whiteRoundedView : UIView = UIView(frame: CGRect(x: 10, y: 8, width: self.view.frame.size.width - 20, height: 120))
whiteRoundedView.layer.backgroundColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0.36, 0.39, 0.40, 1.0])
whiteRoundedView.layer.masksToBounds = false
whiteRoundedView.layer.cornerRadius = 2.0
whiteRoundedView.layer.shadowOffset = CGSize(width: 0, height: 0)
whiteRoundedView.layer.shadowOpacity = 0.0
cell.contentView.addSubview(whiteRoundedView)
cell.contentView.sendSubview(toBack: whiteRoundedView)
cell.emojiLabel.text = cell.emojiString
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toDetails" {
let destinationViewController = segue.destination as! JobDetailViewController
destinationViewController.valueToPass = (sender as? String)!
}
}
My Cell:
import UIKit
import Firebase
class JobTableViewCell: UITableViewCell {
#IBOutlet weak var jobLabel: UILabel!
var job: Job! {
didSet {
jobLabel.text = job.text
}
}
}
Job.Swift:
import Foundation
import Firebase
class Job{
var text: String = ""
let ref: DatabaseReference!
init(text: String) {
self.text = text
ref = Database.database().reference().child("jobs").childByAutoId()
}
init(snapshot: DataSnapshot)
{
ref = snapshot.ref
if let value = snapshot.value as? [String : Any] {
text = value["text"] as! String
}
}
func save() {
ref.setValue(toDictionary())
}
func toDictionary() -> [String : Any]
{
return [
"text" : text,
]
}
}
And in my DestinationController:
import UIKit
import Firebase
class JobDetailViewController: UIViewController {
#IBOutlet weak var jobDetail: RoundText!
var valueToPass: String = ""
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
jobDetail.text = valueToPass
}
override func viewDidLoad() {
super.viewDidLoad()
title = "Jobinformation"
}
}
You should not be using cells to store data. You should have a data model that represents the data you are showing in the cells, and you should use the indexPath of the selected cell to look up the data in your data model.
Quick solution:
Change
performSegue(withIdentifier: "yourSegueIdentifer", sender: self) to performSegue(withIdentifier: "yourSegueIdentifer", sender: valueToPass)
2.Your prepare for Segue method should looks like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "yourSegueIdentifer" {
let destinationViewController = segue.destination as! AnotherViewController
destinationViewController.valueToPass = sender as? String
}
}
On AnotherViewController create var valuteToPass: String = "" and set your label.text = valueToPass
But I think you should not use currentCell.textLabel.text value, instead use the original value. (like if you set your currentCell as cell.textLabel.cell = array[indexPath.row], your valueToPass should be valueToPass = array[indexPath.row])
EDIT:
You use didDeselectRowAt method, instead of didSelectRowAt.
Change func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) to
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
Don't use global variable, create it in didSelectRowAt.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
let valueToPass = currentCell.textLabel?.text
print("value: \(valuteToPass)")
performSegue(withIdentifier: "toDetails", sender: valueToPass)
}
On DestinationController:
class DestinationController: UIViewController {
var valuteToPass: String = ""
override func viewDidLoad() {
super.viewDidLoad()
jobLabel.text = valueToPass
}
}
EDIT2
JobTableViewController
delete var valueToPass:String!
Change let valueToPass = jobs[indexPath.row].text instead of let valueToPass = currentCell.textLabel?.text
I checked this change in your code, this will work.
Hi I cant comment but im using #Dris answer and I kept getting this error that says
Could not cast value of type 'UITableViewCell' (0x115464e18) to 'NSString' (0x10fa594c8).
The SIGABRT targets the line destinationViewController.valueToPass = (sender as? String)!
Why is that?
this is basically my code
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Determine what to do when a cell in a particular section is selected.
print("did select: \(indexPath.row) ")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
valueToPass = currentCell.textLabel?.text
print("valueToPass: \(String(describing: valueToPass))")
performSegue(withIdentifier: "cellToView", sender: self)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell?
if tableView == self.tableView {
let currentNotif = notificationList[indexPath.row]
cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as UITableViewCell
cell?.textLabel?.text = currentNotif.notifType
cell?.detailTextLabel?.text = "\(currentNotif.notifTime) \n\(currentNotif.notifContent)"
}
if tableView == self.tableViewAnnounce {
let currentAnnounce = announcementList[indexPath.row]
cell = tableView.dequeueReusableCell(withIdentifier: "cellAnn", for: indexPath) as UITableViewCell
cell?.textLabel?.text = currentAnnounce.annouceType
cell?.detailTextLabel?.text = "\(currentAnnounce.annouceTime) \n\(currentAnnounce.annouceContent)"
}
return cell!
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "cellToView" {
// perform custom segue operation.
let destinationViewController = segue.destination as! ExtendedAnnouncementViewController
destinationViewController.valueToPass = (sender as? String)!
}
}
I'd avoid using a global variable to pass the data to the destination view controller. Defer the lookup until you are ready to pass the data.
And, avoid using force unwrap, it leads to runtime crashes.
Use something like this instead:
let segueIdentifier = "yourSegueIdentifer"
let labelDataSource: [String] = ["SomeText"]
func label(forIndexPath indexPath: IndexPath) -> String? {
let index = indexPath.row
guard labelDataSource.indices.contains(index) else { return nil }
return labelDataSource[index]
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: segueIdentifier, sender: indexPath)
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
guard segue.identifier == segueIdentifier,
let indexPath = sender as? IndexPath,
let destinationViewController = segue.destination as? AnotherViewController else { return }
destinationViewController.valuePassed = label(forIndexPath: indexPath)
}
Related
I want to pass data from selected cell to ViewController.
With my code just pass data from the last cell no matter which cell is selected.
' func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return guestsList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "guestCell", for: indexPath) as! GuestTableViewCell
let guest: GuestModel
guest = guestsList[indexPath.row]
cell.guestNameLabel.text = guest.guestName
cell.attendingLabel.text = guest.attendingStatus
cell.menuLabel.text = guest.menuStatus
cell.ageLabel.text = guest.ageStatus
cell.tableLabel.text = guest.tableNo
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "toGuestDetails", sender: self
)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toGuestDetails"{
if let indexPath = guestsTableView.indexPathForSelectedRow {
let destinationVC = segue.destination as! GuestDetailsViewController
destinationVC.guestDetail = [guestsList[indexPath.row]]
}
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let guest = guestsList[indexPath.row]
self.guestsList.remove(at: indexPath.row)
self.guestsTableView.reloadData()
self.deleteGuest(id: guest.id!)
}
}
func deleteGuest(id: String){
refGuests.child(id).setValue(nil)
self.guestsTableView.reloadData()
}'
And here is my ViewController with details:
' #IBOutlet var nameLabel: UILabel!
#IBOutlet var emailLabel: UILabel!
#IBOutlet var phoneNoLabel: UILabel!
var ref: DatabaseReference!
let uid = Auth.auth().currentUser?.uid
var guestDetail = [GuestModel]()
override func viewDidLoad() {
super.viewDidLoad()
showGuestDetails()
}
func showGuestDetails(){
ref = Database.database().reference().child("userInfo").child(uid!).child("guests")
ref.queryOrderedByKey().observe(.value) { (snapshot) in
if snapshot.childrenCount>0{
self.guestDetail.removeAll()
for guests in snapshot.children.allObjects as![DataSnapshot]{
let guestObject = guests.value as? [String: AnyObject]
let guestName = guestObject?["guestName"]
let guestFamilyName = guestObject?["guestFamilyName"]
let phone = guestObject?["guestPhoneNumber"]
let email = guestObject?["guestEmail"]
let guestDetails = GuestModel(guestName: guestName as? String, guestFamilyName: (guestFamilyName as? String)!, guestPhoneNumber: phone as? String, guestEmail: email as? String)
let fullName = guestDetails.guestName! + " " + guestDetails.guestFamilyName!
self.nameLabel.text = fullName
self.emailLabel.text = guestDetails.guestEmail
self.phoneNoLabel.text = guestDetails.guestPhoneNumber
self.guestDetail.append(guestDetails)
}
}
}
}
I am implementing a function that sends the title of the cell to the JSON file name of the next controller when clicking on a data table cell.
The data passes well, but the data arrives one by one late. If you click the first cell, the data is not gone, and if you click the second cell, the contents of the first cell are transferred.
Where do I adjust data going late one by one? Any ideas?
vc1
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let nextViewController: SecondViewController = segue.destination as? SecondViewController else {
return
}
guard let cell: UITableViewCell = sender as? UITableViewCell else {
return
}
nextViewController.title = cell.textLabel?.text
nextViewController.secondAssetName = jsonName
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let country: Countries = countries[indexPath.row]
jsonName = country.asset_name
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return countries.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainCell", for: indexPath)
let country: Countries = countries[indexPath.row]
cell.imageView?.image = UIImage(named: "flag_" + country.asset_name)
cell.textLabel?.text = country.korean_name
return cell
}
// Data Transfer
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let nextViewController: SecondViewController = segue.destination as? SecondViewController else {
return
}
guard let cell: UITableViewCell = sender as? UITableViewCell else {
return
}
nextViewController.title = cell.textLabel?.text
nextViewController.secondAssetName = jsonName
}
}
vc2
class SecondViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var weathers = [Weather]()
var secondAssetName: String?
#IBOutlet weak var tableView: UITableView!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let jsonDecoder = JSONDecoder()
guard let dataAsset = NSDataAsset(name: secondAssetName ?? "") else {
return
}
do {
weathers = try jsonDecoder.decode([Weather].self, from: dataAsset.data)
} catch {
print(error.localizedDescription)
}
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return weathers.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: CustomTableViewCell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomTableViewCell
let weather: Weather = weathers[indexPath.row]
switch weather.state {
case 10:
cell.cellImageView?.image = UIImage(named: "sunny.png")
case 11:
cell.cellImageView?.image = UIImage(named: "cloudy.png")
case 12:
cell.cellImageView?.image = UIImage(named: "rainy.png")
case 13:
cell.cellImageView?.image = UIImage(named: "snowy.png")
default:
return cell
}
cell.cityNameLabel.text = weather.city_name
cell.temperatureLabel.text = String(weather.celsius)
cell.rainfallProbabilityLabel.text = String(weather.rainfall_probability)
return cell
}
}
Add breakpoints to your code. You should see where the problem is. prepare(for: sender:) is being called before tableView(_: didSelectRowAt:), so the first time you tap a cell, jsonName is nil during prepare, then it gets set during didSelect. The second time you tap it, jsonName has the value from the first tap, then it gets updated after.
Put all of your logic in one place. Remove the didSelect method, and update prepare like so:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let nextViewController: SecondViewController = segue.destination as? SecondViewController else {
return
}
guard let cell: UITableViewCell = sender as? UITableViewCell else {
return
}
guard let indexPath = tableView.indexPath(for: cell) else {
return
}
let country: Countries = countries[indexPath.row]
nextViewController.title = country.korean_name
nextViewController.secondAssetName = country.asset_name
}
I am trying to make a country weather forecast app, I have vc1 and vc2.
The JSON file to be parsed in vc2 exists for each country, and when the table view cell of vc1 is clicked, we try to implement parsing the JSON file of the country in vc2.
However, I do not know how to pass the JSON file name from vc1 to vc2 through segue.
When passing from vc1 to vc2 using segue, the variable is nil. Is there any solution?
Thanks for reading.
vc1
class MainViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var countries = [Countries]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
let jsonDecoder = JSONDecoder()
guard let dataAsset = NSDataAsset(name: "countries")
else {
return
}
do {
countries = try jsonDecoder.decode([Countries].self, from: dataAsset.data)
} catch {
print(error.localizedDescription)
}
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return countries.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainCell", for: indexPath)
let country: Countries = countries[indexPath.row]
cell.imageView?.image = UIImage(named: "flag_" + country.asset_name)
cell.textLabel?.text = country.korean_name
return cell
}
// Data Transfer
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let nextViewController: SecondViewController = segue.destination as? SecondViewController else {
return
}
guard let cell: UITableViewCell = sender as? UITableViewCell else {
return
}
func name(indexPath: IndexPath) {
let country: Countries = countries[indexPath.row]
nextViewController.title = cell.textLabel?.text
nextViewController.secondAssetName = country.asset_name
}
}
}
vc2
class SecondViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var weathers = [Weather]()
var secondAssetName: String?
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
let jsonDecoder = JSONDecoder()
guard let dataAsset = NSDataAsset(name: secondAssetName ?? " ") else {
return
}
do {
weathers = try jsonDecoder.decode([Weather].self, from: dataAsset.data)
} catch {
print(error.localizedDescription)
}
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return weathers.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: CustomTableViewCell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomTableViewCell
let weather: Weather = weathers[indexPath.row]
switch weather.state {
case 10:
cell.cellImageView?.image = UIImage(named: "sunny.png")
case 11:
cell.cellImageView?.image = UIImage(named: "cloudy.png")
case 12:
cell.cellImageView?.image = UIImage(named: "rainy.png")
case 13:
cell.cellImageView?.image = UIImage(named: "snowy.png")
default:
return cell
}
cell.cityNameLabel.text = weather.city_name
cell.temperatureLabel.text = String(weather.celsius)
cell.rainfallProbabilityLabel.text = String(weather.rainfall_probability)
return cell
}
}
In VC1, when do you navigate to VC2?
If you have connected the VC1 and VC2 in the storyboard please remove that.
You need to call the "performSegueWithIdentifier" method when the user selects a cell.
For this, you need to implement tableview's didSelectRowAtIndexPath. In this method, you need to call the performSegueWithIdentier method to navigate to vc2.
Also,
In the prepare for segue method, you have this code.
func name(indexPath: IndexPath) {
let country: Countries = countries[indexPath.row]
nextViewController.title = cell.textLabel?.text
nextViewController.secondAssetName = country.asset_name
}
Why do you have it as function? and you are not calling the name function here?
You can move the following code outside the name(index path:) function.
Like so :
// Data Transfer
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let nextViewController: SecondViewController = segue.destination as? SecondViewController else {
return
}
guard let cell: UITableViewCell = sender as? UITableViewCell else {
return
}
let country: Countries = countries[indexPath.row]
nextViewController.title = cell.textLabel?.text
nextViewController.secondAssetName = country.asset_name
}
I have two custom table views. I need to pass first and second cell datas of DestinationTableView to first cell of MyCartTableView. How can I make transition between this two table view cells with outside of tableView.
I did tableView.indexPathForSelectedRow but this time I need to make with UIButtonoutside of tableView.
Below triggered with tableView cell.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "cellForFoodSegue" {
if let destinationViewController = segue.destination as? DetailViewController
{
let indexPath = self.mainTableView.indexPathForSelectedRow!
var foodNameArray: String
var foodPriceArray: Double
foodNameArray = foodNames[indexPath.row]
foodPriceArray = foodPrices[indexPath.row].purchaseAmount
destinationViewController.detailFoodName = foodNameArray
destinationViewController.detailFoodPrice = foodPriceArray
}
}
}
I tried below code but I did not success passing data with button.
#IBAction func addBasket(_ sender: Any) {
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "addToCartSegue") {
if let addToCartVC = segue.destination as? MyCartViewController {
let selectedCell = sender as! UITableViewCell
let indexPath = self.detailTableView.indexPath(for: selectedCell)
var foodNameArray: String
var foodPriceArray: Double
foodNameArray = foodNames[indexPath.row]
foodPriceArray = prices[indexPath.row].purchaseAmount
addToCartVC.fromDetailFoodName = foodNameArray
addToCartVC.fromDetailFoodPrice = prices[(indexPath?.row)!].purchaseAmount
}
}
}
Belows my MyViewController codes. Which is my added objects when tapped to addBasket button
class MyCartViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var fromDetailFoodName: [String?] = []
var fromDetailFoodPrice = Double()
var nameLabel = MyCartTableViewCell()
#IBOutlet weak var myCartTableView: UITableView!
#IBOutlet weak var totalPriceLabel: UILabel!
let foodNames = [
"Hamburger big mac",
"Cemal",
"Emre",
"Memo"
]
//TODO-: Delete my cart
#IBAction func deleteMyCart(_ sender: Any) {
}
//TODO: - Approve my cart
#IBAction func approveCart(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return section == 0 ? 1 : foodNames.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCartCell", for: indexPath) as! MyCartTableViewCell
cell.myCartFoodNameLabel?.text = fromDetailFoodName.description
cell.myCartFoodPriceLabel?.text = "\(fromDetailFoodPrice)₺"
return cell
}
}
You should get the index path of the data you want to pass in func addBasket(_ sender: Any).
For example, you can save index path as a property that referenced in class.
class StartViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var selectedIndexPath: IndexPath?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedIndexPath = indexPath
}
#IBAction func addBasket(_ sender: Any) {
if let indexPath = selectedIndexPath {
let destinationVC = MyCartViewController()
destinationVC.detailFoodName = foodNames[indexPath.row]
destinationVC.detailFoodPrice = foodPrices[indexPath.row].purchaseAmount
}
}
}
In MyCartViewController which is destination VC.
class MyCartViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var fromDetailFoodNames: [String?] = []
var fromDetailFoodPrices: [Double?] = []
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 1 && indexPath.last! <= fromDetailFoodPrices.indices.last! {
let cell = myCartTableView.dequeueReusableCell(withIdentifier: "myCartCell", for: indexPath) as! MyCartTableViewCell
let name = fromDetailFoodNames[indexPath.row]?.description ?? ""
let price = fromDetailFoodPrices[indexPath.row]
cell.myCartFoodNameLabel?.text = name
cell.myCartFoodPriceLabel?.text = "\(price)₺"
return cell
}
}
}
BTW, for better coding, you can implement OOP concept in your code. detailFoodName and detailFoodPrice should be in ONE object. Besides, var foodNameArray: String naming could be confusing. Rename it as var foodName: String would be better.
I want to pass the storage data contained in a TableView and pass it to another ViewController. My customCell has an UIImage and a String.
When the user press the cell I want to show a "detail view controller" with the UIImage and a label containing the info of the cell selected.
Here is my code:
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var dataTableView: UITableView!
var myList = [dataList]()
var textToBeSent: String = ""
var selectedImage: UIImage?
var selectedLabel: String?
//Load Items To List
func loaditems(){
let item1 = dataList(photoList: UIImage.self(), itemDescription: "Descripcion Aqui")
let item2 = dataList(photoList: UIImage.self(), itemDescription: "Aqui tmb")
myList += [item1,item2]
}
//var list = ["Documento 1", "Documento 2", "Documento 3"]
override func viewDidLoad() {
super.viewDidLoad()
if let savedData = loadSavedItems(){
myList += savedData
} else {
loaditems()
}
//dataTableView.register(UITableViewCell.self, forCellReuseIdentifier: "reusablecell")
// Do any additional setup after loading the view, typically from a nib.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return myList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "prototype", for: indexPath) as! PrototypeCell
let itemsinCell = myList[indexPath.row]
cell.imageItem.image = itemsinCell.photoList
cell.itemDescription.text = String(itemsinCell.itemDescription)
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete{
myList.remove(at: indexPath.row)
dataTableView.reloadData()
}
saveToSavedData()
}
Here is the func where I want to pass the data of a certain cell.
The data is from a Swift file stored in a "DataList" using aDecoder NSCoder.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Row \(indexPath.row) selected")
selectedImage! = myList[indexPath.row].photoList
selectedLabel! = myList[indexPath.row].itemdescription
performSegue(withIdentifier: "selectedRowSegue", sender: myList[indexPath.row])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "selectedRowSegue"){
let chosenRowViewController = segue.destination as! chosenRowViewController
chosenRowViewController.image3 = selectedImage?.photoList
chosenRowViewController.label3 = selectedLabel?.itemDescription
}
}
Unwind a segue in order to fill up the cells with data from previous
ViewController:
//Unwinde Segue
#IBAction func unWindlToList(sender: UIStoryboardSegue){
if let sourceViewController = sender.source as? ProcessViewController, let item = sourceViewController.item{
let newIndexPath = IndexPath(row: myList.count, section: 0)
myList.append(item)
dataTableView.insertRows(at: [newIndexPath], with: .automatic)
}
saveToSavedData()
}
//Archive Data
func saveToSavedData(){
NSKeyedArchiver.archiveRootObject(myList, toFile: (dataList.fileFolder?.path)!)
}
//Unarchive Data
func loadSavedItems() -> [dataList]?{
return NSKeyedUnarchiver.unarchiveObject(withFile: (dataList.fileFolder?.path)!) as? [dataList]
}
}
class PrototypeCell: UITableViewCell {
#IBOutlet weak var itemDescription: UILabel!
#IBOutlet weak var imageItem: UIImageView!
}
just replace prepare(for segue: UIStoryboardSegue, sender: Any?) function with below code
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "selectedRowSegue"), let list = sender as? dataList {
let chosenRowViewController = segue.destination as! chosenRowViewController
chosenRowViewController.image3 = list.photoList
chosenRowViewController.label3 = list.itemDescription
}
}
There are several things that stand out.
1- var myList = [dataList]() dataList is a Class, classes should be capitalized. It should be var myList = [DataList]()
2- You have this as a class property but it's not used anywhere in the code you posted so why did you add it and what is it's purpose? var textToBeSent: String = ""
3- You have these 2 class property variables
var selectedImage: UIImage?
var selectedLabel: String?
to hold the data from [myList] but you really don't need them because you can just access the data from [myList] using dot notation inside prepareForSegue (read the commented out code in prepareForSegue).
4- In prepareForSegue you have let chosenRowViewController = segue.destination as! chosenRowViewController. ChosenRowViewController is a class and it should be capitalized like so:
let chosenRowViewController = segue.destination as! ChosenRowViewController // it's capitalized after the as!
Here's the code a little cleaned up.
#IBOutlet weak var dataTableView: UITableView!
var myList = [DataList]()
func loaditems(){
let item1 = dataList(photoList: UIImage.self(), itemDescription: "Descripcion Aqui")
let item2 = dataList(photoList: UIImage.self(), itemDescription: "Aqui tmb")
myList.append(item1)
myList.append(item2)
}
override func viewDidLoad() {
super.viewDidLoad()
// everything you already have inside here...
}
// your tableView datasource methods...
5- Since your using prepareForSegue you don’t need didSelectRowAt indexPath
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "selectedRowSegue"){
// get the indexPath for the selectedRow
let indexPath = tableView.indexPathForSelectedRow
let chosenRowViewController = segue.destination as! ChosenRowViewController
chosenRowViewController.image3 = myList[indexPath!.row].photoList // use dot notation to access the photoList property
chosenRowViewController.label3 = myList[indexPath!.row].itemDescription // use dot notation to access the itemDescription property
}
}
You have made a mistake in your didSelectRowAt, while calling performSegue you have to pass the controller which is the sender, in your case it is your current UIVIewController, so you have to write self as sender controller. So your corrected methog will look like below code snippet:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Row \(indexPath.row) selected")
selectedImage! = myList[indexPath.row].photoList
selectedLabel! = myList[indexPath.row]
performSegue(withIdentifier: "selectedRowSegue", sender: self)
}