How to get value from table view cell - swift

I need to take price from table view cell
I need to get it another VC
I need to transfer price each time when user selected row
The problem is that I don't know how correctly get the value from tableView
MenueViewController:
import UIKit
class MenueViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var dishes: [Dish] = []
var totalSum = 0
override func viewDidLoad() {
super.viewDidLoad()
dishes = createArray()
tableView.delegate = self
tableView.dataSource = self
tableView.backgroundColor = .white
navigationItem.title = "Меню"
}
func createArray() -> [Dish] {
var tempDishes: [Dish] = []
let dish1 = Dish(image: UIImage.init(named: "plovKebab")!, title: "Плов Кебаб", price: 169, type: "Основные Блюда")
let dish2 = Dish(image: UIImage.init(named: "plovKebabShafran")!, title: "Плов Кебаб Шафран", price: 169, type: "Основные Блюда")
tempDishes.append(dish1)
tempDishes.append(dish2)
return tempDishes
}
}
extension MenueViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dishes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let dish = dishes[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "MenueCell") as! MenueCell
cell.setDish(dish: dish)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let dish = dishes[indexPath.row]
totalSum += dish.price //the place where I tried to take price
print(totalSum)
}
}
VC where we need to take that price:
import UIKit
class OrderViewController: UIViewController {
#IBOutlet weak var totalPriceLabel: UILabel!
var totalPrice: MenueViewController?
var sum:Int?
override func viewDidLoad() {
super.viewDidLoad()
if let price = sum {
totalPriceLabel.text = String(totalPrice?.totalSum)
}
}
The value of sum is 0
How can I get the value?

Please note that the didSelectRowAtIndexpath will not get called before your performSegue. At this link, you can find a detailed explanation including a sample code about your problem and how it can be solved.
Also, there are multiple approaches listed below to solve your problem.
Use willSelectIndexpath and capture the indexpath to pass it in performSegue method
Use didSelectIndexpath and perform segue inside
Use only didSelectIndexpath and perform the navigation logic inside it with out segues

Related

Passing data into UITableView

I am new to swift and have been experimenting with passing data between view controllers. I have been attempting to pass data from a view controller into a UITableViewCell, However once run my code has no effect.
DetailViewController
(passes data to the libraryViewController)
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let DestViewController: LibraryMovieViewController = segue.destination as! LibraryMovieViewController
DestViewController.movieTitle = movieTitle
DestViewController.movieRelease = movieReleaseDate
}
}
UITableViewCell
class MovieSearchTableViewCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var posterView: UIImageView!
#IBOutlet weak var overviewLabel: UILabel!
}
LibraryViewController
struct libMovie {
//let mainImage: UIImage
let title: String
let release: String
}
class LibraryMovieViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var dataSource: [libMovie] = []
var movieTitle: String!
var movieRelease: String!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
// Do any additional setup after loading the view.
loadDataSource()
}
func loadDataSource(){
// dataSource.append(libMovie(title: " \(movieTitle)", release: " \(movieRelease)"))
}
}
extension LibraryMovieViewController: UITableViewDataSource, UITableViewDelegate{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 115
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let movieCell = tableView.dequeueReusableCell(withIdentifier: "libCell", for: indexPath) as? LibraryMovieTableViewCell else {
return UITableViewCell()
}
let libMovie = dataSource[indexPath.row]
movieCell.cellTitleLabel.text = "Movie Title: \(movieTitle)"
movieCell.cellReleaseLabel.text = "Release Date: \(movieRelease)"
return movieCell
}
}
I would expect that when the app is run that movieTitle and movieReleaseDate are passed from the detail view controller and input into the library table cell, this is initiated by tapping a button on the detail view controller.
However this seems to have no affect on the program or simply returns blank cells.
No errors are reported in console nor does the app crash
In prepareForSegue you've passed data to movieTitle and movieRelease, while you're using dataSource to inflate data on tableView.
Either pass and object of [libMovie] to DestViewController.dataSource if you want to inflate multiple rows of different movies or return 1 in numberOfRowsInSection and pass set the label content in cell with movieTitle and movieRelease.
In your code, numberOfRowsInSection is returning dataSource.count, but doesn't seem to pass that data to this view controller.
You can either hardcode the numberOfRowsInSection as 1 or pass the dataSource from the first view controller and update your cellForRowAtIndexPath method.
The data source in the destination view controller is empty, what do you expect?
Uncomment the line in loadDataSource and reload the table view.
func loadDataSource(){
dataSource.append(ibMovie(title: movieTitle, release: movieRelease))
tableView.reloadData()
}
And replace
movieCell.cellTitleLabel.text = "Movie Title: \(movieTitle)"
movieCell.cellReleaseLabel.text = "Release Date: \(movieRelease)"
with
movieCell.cellTitleLabel.text = "Movie Title: \(libMovie.title)"
movieCell.cellReleaseLabel.text = "Release Date: \(libMovie.release)"

Is this ok when implementing multiple table views in Swift5?

I'm implementing two table views on a single view controller in Swift 5. I realise this can be done with a single extension to the view controller by identifying the table views by name. However, I'd like to keep the code separate, and so I've done it as shown in this simple example here - this has involved looking at several examples on the web and so a general thank you to all who post these things! The main one is the answer by Girish Ghoda here:
Two tables on one view in swift
It all seems to work, but I'm wondering if I'm breaking any important rules...
There are two table views on the view controller, with simple constraints and outlets tableView1 and tableView2.
This is the ViewController.swift file
import UIKit
var array1 = ["one", "two", "three"]
var array2 = ["left", "right", "centre", "outside"]
class ViewController: UIViewController {
#IBOutlet weak var tableView1: UITableView!
#IBOutlet weak var tableView2: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
initTableViews()
}
var dataSource1: DataSource1!
var dataSource2: DataSource2!
func initTableViews() {
dataSource1 = DataSource1()
tableView1.dataSource = dataSource1
tableView1.delegate = dataSource1
dataSource2 = DataSource2()
tableView2.dataSource = dataSource2
tableView2.delegate = dataSource2
}
}
There are then two files:
TableViewClass1.swift
import UIKit
class DataSource1: NSObject, UITableViewDataSource, UITableViewDelegate {
override init(){
super.init()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array1.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath)
cell.textLabel?.numberOfLines = 0
cell.textLabel?.text = array1[indexPath.row]
return cell
}
}
TableViewClass2.swift:
import UIKit
class DataSource2: NSObject, UITableViewDataSource, UITableViewDelegate {
override init(){
super.init()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array2.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath)
cell.textLabel?.numberOfLines = 0
cell.textLabel?.text = array2[indexPath.row]
return cell
}
}
As I say, everything seems ok, so it may seem strange to ask about it, but while the approach seems to give simple code structure, I'm wondering if there is anything here that could lead to problems.
Many thanks,
Ian
It's fine, but if both classes are that redundant I would use one DataSource class with a convenience initializer and lazy instantiated dataSource properties
class DataSource: NSObject, UITableViewDataSource, UITableViewDelegate {
var array : [String]
let cellIdentifier : String
init(array: [String], cellIdentifier : String )
{
self.array = array
self.cellIdentifier = cellIdentifier
super.init()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
cell.textLabel?.numberOfLines = 0
cell.textLabel?.text = array[indexPath.row]
return cell
}
}
lazy var dataSource1: DataSource = {
return DataSource(array: array1, cellIdentifier: "cell1")
}()
lazy var dataSource2: DataSource = {
return DataSource(array: array2, cellIdentifier: "cell2")
}()
func initTableViews() {
tableView1.dataSource = dataSource1
tableView1.delegate = dataSource1
tableView2.dataSource = dataSource2
tableView2.delegate = dataSource2
}

My TableView's not changed after reload data

I wrote a class that let me create multiple tableView simply. When I call this class for the first time, everything work well. But when I change some data, and reload the table, nothing changed.
Sample code:
class TestViewController: UIViewController {
var arrData = ["a","b","c"]
var myTableView: MyTableView?
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
myTableView = MyTableView(table: tableView, data: arrData)
}
#IBAction func buttonTapped(_ sender: UIButton) {
arrData = ["d","e","f"]
myTableView!.tableView.reloadData() //=> Not change anything
}
}
class MyTableView: NSObject, UITableViewDataSource {
var tableView: UITableView
var data: Array<String>
init(table: UITableView, data: Array<String>) {
self.data = data
self.tableView = table
super.init()
self.tableView.dataSource = self
self.tableView.register(MyTableViewCell.self, forCellReuseIdentifier: "myCell")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.data.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
cell.textLabel!.text = self.data[indexPath.row]
return cell
}
}
class MyTableViewCell : UITableViewCell {
//something here
}
When the view was loaded, the table has 3 rows: a,b,c. When I tap the button, nothing changed (expected: d,e,f)
Please help me!
Swift arrays are copied by value so the line self.data = data will take a copy of your array. Later changing the array contents of the source will not be reflected in the copy in your MyTableView.
You'll need to pass the array over again and take a second copy to update the table, e.g. write a method in MyTableView similar to the following:-
func setNewValues(data: Array<String>)
{
self.data = data
self.tableView.reloadData()
}
and call that from your buttonTapped function, i.e.:
#IBAction func buttonTapped(_ sender: UIButton) {
arrData = ["d","e","f"]
myTableView!.setNewValues(data: arrData)
}
Be careful with the force-unwrapped myTableView though - I'd replace that '!' with '?'.

What am I doing wrong on trying to set data to Struct

I'm trying to create a Struct to be able to pass data between screens. However, when I set data to my Struct properties and then I try to use it in another screen I got the value as "nil". What am I doing wrong?
Objects Struct:
import UIKit
struct Objetos {
var nome: String
var foto: UIImage
}
That's how I'm trying to set the value:
var objeto = Objeto(nome: "", foto: UIImage())
#IBAction func botaoAdcItem(_ sender: UIButton) {
if (self.namePreview!.text != nil) && (self.imagePreview!.image != nil) {
objeto?.nome = self.namePreview.text!
objeto?.foto = self.imagePreview.image!
self.navigationController?.popViewController(animated: true)
}
else { return }
}
That's how I'm trying to read the data:
class ViewController: UIViewController, CLLocationManagerDelegate, UITableViewDataSource {
#IBOutlet weak var itensTableView: UITableView!
var arrayNomes = NSMutableArray()
var objeto: Objetos?
var objetos = [Objetos]()
//TableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
for nome in objetos {
arrayNomes.add(nome)
}
return arrayNomes.count //Nil value
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = itensTableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! tableviewCell
cell.nameCell.text = objeto?.nome //Nil value
cell.imageViewCell.image = objeto?.foto //Nil value
return cell
}
override func viewDidAppear(_ animated: Bool) {
self.itensTableView.reloadData()
}
Storyboard:
In your second code snippet, you never initialize objetos to anything. Thus, when you try to set one of the properties on objetos, nothing happens, because objetos doesn't exist.
Solution: Initialize objetos before trying to use it.
You can try this if you want to set the data to the previous VC
// ! won't crash
let count = self.navigationController.viewControllers!.count
let tab = self.navigationController.viewControllers![count-2] as! UITabBarController
let vc = tab.viewControllers![0] as! ViewController
vc.objetos.append(Objetos(name:self.namePreview.text!,foto:self.imagePreview.image!))
self.navigationController?.popViewController(animated: true)
plus
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = objectos[indexPath.row]
let cell = itensTableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! tableviewCell
cell.nameCell.text = item.nome //Nil value
cell.imageViewCell.image = item.foto //Nil value
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objetos.count
}
Try to replace
var objetos:Objetos?
with
var objeto = Objetos(nome: "", foto: UIImage())
you are starting your object as optional, and thus from the start is nil. When you try to assign properties in your struct you are failures because the object from the beginning is nonexistent
Regarding you other nil values, here is a revised view of your snippet
class ViewController: UIViewController, CLLocationManagerDelegate, UITableViewDataSource {
#IBOutlet weak var itensTableView: UITableView!
var objetos = [Objetos]()
//TableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objetos.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = itensTableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath) as! tableviewCell
let objeto = objectos[indexPath.row]
cell.nameCell.text = objeto.nome
cell.imageViewCell.image = objeto.foto
return cell
}
override func viewWillAppear(_ animated: Bool) {
self.itensTableView.reloadData()
}
In your Additional Novo Itens Vc you are going to need to pass the created data back to your original View Controller, one way to do this is
#IBAction func botaoAdcItem(_ sender: UIButton) {
if (self.namePreview!.text != nil) && (self.imagePreview!.image != nil) {
// create the objecto
var objeto = Objetos(nome: namePreview.text,
foto: imagePreview.image)
// create a reference to your previous view controller
let vc = self.navigationController?.viewControllers[0] as! ViewController
// add objecto to objectos
vc.objetos.append(objeto)
self.navigationController?.popViewController(animated: true)
}
else { return }
}

Changing Labels in DetailVC from tableView

I’m trying to make changes to a label in a DetailViewController, depending on which row we click:
That is how my test App looks
For example, if we click Ferrari I want to display:
“Wow , it's a beautiful red Ferrari of the year… blablabla..”
But if you click another instead:
“I’m the label of the…. car”
Essentially, one description for each car.
How do I change the label?
My code :
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var valueToPass : String!
//crear un color
let medOrange: UIColor = UIColor(red: 0.973, green: 0.388, blue: 0.173, alpha: 1)
var vehicleData : [String] = ["Ferrari 458" , "Lamborghini Murcielago" , "Bugatti Veyron", "Mercedes Benz Biome"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var nib = UINib(nibName: "TableViewCell", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "cell")
//Propiedades de la tableView
self.tableView.backgroundColor = medOrange
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return vehicleData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:TableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as TableViewCell
cell.lblCarName.text = vehicleData[indexPath.row]
cell.imgCar.image = UIImage(named: vehicleData[indexPath.row])
cell.backgroundColor = medOrange
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("Has seleccionado la celda #\(indexPath.row)!")
//Obtener la label de la celda
let indexPath = tableView.indexPathForSelectedRow()
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!
valueToPass = currentCell.textLabel?.text
performSegueWithIdentifier("DetailView", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "DetailView") {
var vc = segue.destinationViewController as DetailViewController
vc.passedValue = valueToPass
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100
}
import UIKit
class DetailViewController: UIViewController {
var passedValue : String!
#IBOutlet weak var lblDetail: UILabel!
#IBOutlet weak var imgDetail: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
From what I can see you did all well and the only part missing in your code is setting passedValue to label in your DetailViewController so just add the following in viewDidLoad method
override func viewDidLoad() {
super.viewDidLoad()
lblDetail.text = passedValue
}
EDITED:
To pass description to DetaliViewContorller you have to store it somehow first and the best way to do it is by declaring a struct that holds both name and description
struct Vehicle {
var vehicleName : String
var vehicleDescription : String
}
Then your vehicleData array should contain objects of type Vehicle
var vehicleData : [Vehice] = [Vehicle(vehicleName : "Ferrari 458",vehicleDescription : "Ferrari desc" ), //Add all vehicles like this...]
And finally passedValue must be initialized like this
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
valueToPass = vehicleData[indexPath.row].vehicleDescription
performSegueWithIdentifier("DetailView", sender: self)
}
its Simple do the following steps:
add one more array with the details text in the details view controller.
when user click on the row you need to get the index.row and pass it to your details view controller.
retrive data from your number 1 step array using index path.
add it to your Lable.
and must set your Lable.text in the ViewDidLoad method.