How to press on a tableview cell to present a view controller with the text in navigation controller - ios

Essentially I have a view controller called FirstViewController, this view controller contains a table view within it called listTableView.
I would like to tap on one of the cells in the table view listTableView and present whatever text was in the cell as the navigation controller title.
The navigation controller that appears when the cell is tapped is called showDetailsViewController.
How can this be done?
The following is what I have written in the FirstViewController
import UIKit
import AudioToolbox
class FirstViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, FeedModelProtocol {
var feedItems: NSArray = NSArray()
var selectedStock : StockModel = StockModel()
let tableView = UITableView()
#IBOutlet weak var listTableView: UITableView!
#IBOutlet weak var refreshButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
//set delegates and initialize FeedModel
self.listTableView.delegate = self
self.listTableView.dataSource = self
let feedModel = FeedModel()
feedModel.delegate = self
feedModel.downloadItems()
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
#IBAction func reloadData(_ sender: Any) {
print("reload pressed")
listTableView.reloadData()
viewDidLoad()
_ = AudioServicesPlaySystemSound(1519)
}
func itemsDownloaded(items: NSArray) {
feedItems = items
self.listTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of feed items
print("item feed loaded")
return feedItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Retrieve cell
let cellIdentifier: String = "stockCell"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
myCell.textLabel?.textAlignment = .center
myCell.textLabel?.font = .boldSystemFont(ofSize: 18)
// Get the stock to be shown
let item: StockModel = feedItems[indexPath.row] as! StockModel
// Configure our cell title made up of name and price
let titleStr = [item.customer].compactMap { $0 }.joined(separator: "-")
print(titleStr)
// Get references to labels of cell
myCell.textLabel!.text = titleStr
return myCell
}
}
UPDATE:
What is the issue with this code:
NOTE:
The restoration id of the tableview is scheduleTable
var homeworkIdentifierFromTableViewCell = ""
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
homeworkIdentifierFromTableViewCell = feedItems[indexPath.row].myCell
self.performSegue(withIdentifier: "scheduleTable", sender: self)
listTableView.deselectRow(at: indexPath, animated: true)
}
UPDATE 2
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item: StockModel = feedItems[indexPath.row] as! StockModel
let titleStr = [item.customer].compactMap { $0 }.joined(separator: "-")
print(titleStr)
}

You can use the didSelectRowAt to notice what cell was clicked and store what the text in the cell was (homeworkArray is the list of cells from a struct. Homeworkidentifier is a value in the struct).
var homeworkIdentifierFromTableViewCell = ""
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
homeworkIdentifierFromTableViewCell = homeworkArray[indexPath.row].homeworkIdentifier
self.performSegue(withIdentifier: "homeworktoExpandHomework", sender: self)
homeworkTableView.deselectRow(at: indexPath, animated: true)
}
Then, you could use a prepare for a segue function to pass the text of the table view cell to the next view controller. You do this by creating a variable in the other view controller (the one that you are going to pass data to) and later accessing it from the other view controller and changing its value.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "reportBug" {
let destinationViewController = segue.destination as! WebViewController
destinationViewController.reason = "reportBug"
}
else if segue.identifier == "provideFeedback" {
let destinationViewController = segue.destination as! WebViewController
destinationViewController.reason = "provideFeedback"
}
}
Here is more about passing data between viewcontrollers : Passing data between View Controllers in Swift (From TableView to DetailViewController)
Hope this helps
EDIT:
Here is the struct I am using :
struct homeworkTableViewCellData {
let homeworkName : String!
let className : String!
let dateName : String!
let colorImage : UIImage!
let homeworkIdentifier : String!
}
I have initialized my homeworkArray with this struct. When I am calling a value from the cell, I am picking one from in the struct.
To set the table view with a struct is more organized. This is a good video that teaches you how to set it up (if you are want to do that) : https://www.youtube.com/watch?v=zAWO9rldyUE&list=LL--UalPCi7F16WzDFhMEg7w&index=20&t=921s

Related

Instantiate view/ do segue and pass data from button's title

If I click button I want to pass label in button to the next view. Next view need navigationItem title with label from buttons in previous view.
I've tried segues, but it didn't work. I am trying with instantiate view now, but it also doesn't work (next View appear but without title and needed data)
**HomeTableViewController**
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "workoutFirstCell", for: indexPath) as! WorkoutTableCell
cell.workoutField?.setTitle(workouts[indexPath.row].workoutTitle, for: .normal)
cell.index = indexPath
cell.cellDelegate = self
return cell
} func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "workoutFirstCell", for: indexPath) as! WorkoutTableCell
cell.workoutField?.setTitle(workouts[indexPath.row].workoutTitle, for: .normal)
cell.index = indexPath
cell.cellDelegate = self
return cell
}
extension HomeTableViewController:WorkoutTableCellDelegate {
func onClickCell(index: Int) {
if let vc = storyboard?.instantiateViewController(withIdentifier: "allWorkouts") as? AllWorkoutsViewController {
vc.titleValue = workouts[index].workoutTitle
vc.tableView.reloadData()
}
}
}
Here is my cell controller
protocol WorkoutTableCellDelegate {
func onClickCell(index: Int)
}
class WorkoutTableCell: UITableViewCell {
var cellDelegate:WorkoutTableCellDelegate?
var index: IndexPath?
var item = AllWorkoutsViewController().navigationItem.title
#IBOutlet weak var workoutField: UIButton!
let user = Auth.auth().currentUser
let db = Firestore.firestore()
var models: [DataCell] = []
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func buttonTapped(_ sender: Any) {
cellDelegate?.onClickCell(index: (index?.row)!)
}
And View Controller where I want to display navigationItemTitle with label from button in previous view
class AllWorkoutsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var titleValue: String = ""
let user = Auth.auth().currentUser
var db = Firestore.firestore()
var models: [DataCell] = []
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
navigationItem.title = titleValue
You should try this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: nil)
if segue.identifier == "youSegue" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let controller = segue.destination as! YourViewController
controller.title = workouts[indexPath.row]
}
}
}

How to make data transition between table view cells with where button out of table view?

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.

Get Switch State from specific cell

I am working on a iOS app that has two ViewControllers. The first is a TableView which creates a row for each index in a array. Each cell of this TableView shows the content in the array corresponding to the index and has a switch. The second ViewController has an image and a label and they are supposed to change depending the switch state. So, how can I get the switch state from a specific cell?
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var state_label: UILabel!
#IBOutlet weak var descr_label: UILabel!
#IBOutlet weak var myimg: UIImageView!
let arr: [String] = ["Text1","Text2", "Text3", "Text4"]
var switch_isOn = false
override func viewDidLoad() {
super.viewDidLoad()
if(switch_isOn == false){
myimg?.image = UIImage(named: "img1")
}else{
myimg?.image = UIImage(named: "img2")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
cell.textLabel?.text = arr[indexPath.row]
let mySwitch = UISwitch()
cell.accessoryView = mySwitch
mySwitch.tag = 1001
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let segueIdentifier: String
segueIdentifier = "segue"
// Get the selected Cell and Iterate through it's subviews to find the switch using the tag
let cell = tableView.cellForRow(at: indexPath)
//Get the Cell Text
print("\(cell?.textLabel?.text ?? "")")
// Iterate through subviews of Cell
for v in cell?.subviews ?? [] {
// If a view found with tag == 1001 then it's the switch view because we had assigned 1001 to the switch view
if v.tag == 1001 {
// One last check we cast the view to UISwitch if it succeed then it's the switch view
if let mySwitch = v as? UISwitch {
if(mySwitch.isOn == true){
descr_label?.text = "\(cell?.textLabel?.text ?? "")"
print("The cell has the Switch On")
switch_isOn = true
}else{
descr_label?.text = "\(cell?.textLabel?.text ?? "")"
switch_isOn = false
print("The cell has the Switch Off")
}
}
}
}
self.performSegue(withIdentifier: segueIdentifier, sender: self)
}
}
Using the accessory view for the switch seems to be an easy solution but it's very cumbersome to access the view. Something like for v in cell?.subviews ?? [] and dealing with tags is horrible.
A better more efficient solution is a custom cell class.
In Interface Builder set the style of the cell to custom and drag an UILabel and an UISwitch into the canvas. Set the class of the cell to TableViewCell.
Add a new CocoaTouch class TableViewCell as subclass of UITableViewCell. You need two IBOutlets, one IBAction and a callback variable. The callback is important to keep the state of the switch in the model. Connect the outlets and the action in IB.
class TableViewCell: UITableViewCell {
#IBOutlet weak var switcher : UISwitch!
#IBOutlet weak var label : UILabel!
var callback : ((Bool)->())?
#IBAction func switchChanged(_ sender : UISwitch) {
callback?(sender.isOn)
}
}
Create a data source model containing the text and the state of the switch
struct Item {
var text : String
var isSelected : Bool
init(text : String, isSelected : Bool = false {
self.text = text
self.isSelected = isSelected
}
}
Declare the data source array
var arr : [Item] = [Item(text: "Text1"), Item(text: "Text2"), Item(text: "Text3"), Item(text: "Text4")]
Replace cellForRow with
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
let item = arr[indexPath.row]
cell.label.text = item.text
cell.switcher.isOn = item.isSelected
// the callback updates the model and is called when the value of the switch changes
cell.callback = { newValue in
item.isSelected = newValue
}
return cell
}
Replace didSelectRow with (yes, it's only one line, it passes the index path as sender parameter)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "segue", sender: indexPath)
}
Finally Implement prepare(for segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segue" {
let viewController = segue.destination as! ViewController // the class of the second view controller
// get the current index path
let indexPath = sender as! IndexPath
let item = arr[indexPath.row]
// get the state of the switch from the model, not from the view
let isSelected = item.isSelected
// do something with `isSelected`
}
}
}
In order to achieve what you want properly, you're going to want to set up a custom cell.
An example of this is below, and assumes a Storyboard/XIB UI:
import UIKit
class SwitchTableViewCell: UITableViewCell {
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var contentSwitch: UISwitch!
// So that we can identify the cell in our table view controller.
static let identifier: String {
return String(describing: type(of: self))
}
}
in order to use this with your table view. you will have to register the cell for use in SwitchTableViewController.viewDidLoad():
tableView.register(SwitchTableViewCell.self, forCellReuseIdentifier: SwitchTableViewCell.identifier)
Next, you're going to want to modify cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(SwitchTableViewCell.identifier, forIndexPath: indexPath) as! SwitchTableViewCell
cell.textLabel?.text = arr[indexPath.row]
// cell.contentSwitch will be setup as an outlet via Storyboard / XIB.
return cell
}
after that's done, go ahead and add a variable to SwitchTableViewController:
fileprivate var selectedState: UIControl.State?
And update didSelectRowAt to store the state from the cell:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! SwitchTableViewCell
selectedState = cell.contentSwitch.state
segueIdentifier = "segue" // probably want a more meaningful segue name here.
self.performSegue(withIdentifier: segueIdentifier, sender: self)
}
finally, override prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segue" {
if let vc = segue.destination as? ContentViewController { // cast accordingly, 'ContentViewController' is placeholder
// pass the state to the destination view controller
vc.state = selectedState
selectedState = nil
}
}
}
and that's you done!
There are many ways to read the state of a switch from a cell. You can create a custom cell class and access that using IBOutlets and even you can use delegates to get back from the Custom Cell class back to your View Controller. If you're using this code for learning purposes it's Ok to use and add any types of Controls to the cell like this but in real project you might try Custom cells.
See the commented areas in the code
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var myimg: UIImageView!
var arr: [String] = ["bla", "blablabla", "blabla"]
override func viewDidLoad() {
super.viewDidLoad()
myimg?.image = UIImage(named: "Image1")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default,
reuseIdentifier: "cell")
cell.textLabel?.text = arr[indexPath.row]
let mySwitch = UISwitch()
// Add a tag to your switch so later on you can access the switch using this tag
mySwitch.tag = 1001
cell.accessoryView = mySwitch
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
segueIdentifier = "segue"
// Get the selected Cell and Iterate through it's subviews to find the switch using the tag
let cell = tableView.cellForRow(at: indexPath)
// Iterate through subviews of Cell
for v in cell?.subviews ?? [] {
// If a view found with tag == 1001 then it's the switch view because we had assigned 1001 to the switch view
if v.tag == 1001 {
// One last check we cast the view to UISwitch if it succeed then it's the switch view
if let mySwitch = v as? UISwitch {
// Here you can get the state of the switch
let switchState = mySwitch.state
}
}
}
self.performSegue(withIdentifier: segueIdentifier, sender: self)
}
}
As I said this is not the best way to add and read views using tags but still good to know that you can
Edit:
Here is the complete solution for your project to work. You already have a ViewController but you don't have a DetailViewController to which you want to segue
View Controller Code
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let rooms: [String] = ["Kitchen","Living Room", "Master's Bedroom", "Guest's Bedroom"]
let segueIdentifier = "segueIdentifier"
var switch_isOn = false
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rooms.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
cell.textLabel?.text = rooms[indexPath.row]
let mySwitch = UISwitch()
cell.accessoryView = mySwitch
mySwitch.tag = 1001
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Get the selected Cell and Iterate through it's subviews to find the switch using the tag
let cell = tableView.cellForRow(at: indexPath)
// Iterate through subviews of Cell
for v in cell?.subviews ?? [] {
// If a view found with tag == 1001 then it's the switch view because we had assigned 1001 to the switch view
if v.tag == 1001 {
// One last check we cast the view to UISwitch if it succeed then it's the switch view
if let mySwitch = v as? UISwitch {
// Assign the current state of the switch to switch_isOn variable
self.switch_isOn = mySwitch.isOn
}
}
}
self.performSegue(withIdentifier: segueIdentifier, sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueIdentifier {
if let detailViewController = segue.destination as? DetailViewController {
// used guard let to be on safe side
guard let indexPath = sender as? IndexPath else { return }
// pass in the data needs to the detail view controller
detailViewController.descr = rooms[indexPath.row]
detailViewController.isOn = switch_isOn
}
}
}
}
Detail View Controller Code
import UIKit
class DetailViewController: UIViewController {
#IBOutlet weak var descr_label: UILabel!
#IBOutlet weak var state_label: UILabel!
#IBOutlet weak var myImageView: UIImageView!
var descr = ""
var isOn = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
descr_label.text = descr
// for UIImage you can use UIImage(named: "on_image") but i have used the imageLiteral which is pics directly the image from xcassets
myImageView.image = isOn ? #imageLiteral(resourceName: "on_image") : #imageLiteral(resourceName: "off_image")
state_label.text = isOn ? "Switch is ON" : "Switch is Off"
}
}
Example Project download here

How extract from UITableView data (UIImage and String) to another ViewController

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)
}

How to reload a view-controller after data has been fetched from a network request?

I have a problem and can't seem to fix it after looking at tutorials online and other SO questions with a similar problem, which leaves me to think I've done something wrong/bad practice related in my code.
I have 2 table view controllers.
The first TableViewController is populated from a database, all this works fine. When I click one of the cells it segues to a second TableViewController which also should be populated from a database (depending on what you select in the first VC).
Currently if I click a cell in TVC1 it goes to TVC2 and it's empty, then it I click back within my navigation controller and select something else, it goes back to TVC2 and shows me my first selection. This indicates that TVC2 is being loaded before the network has returned its data from the database.... so, I tried using tableView.reloadData() in various places like viewDidLoad and viewDidAppear, but i just can't seem to get it to work.
Below is both TVC's. I've stuck with MVC design pattern and haven't included the model and severConnection code for each TVC because I don't want to over complicate the post, however if you'd like to see either I will update.
Thanks in advance for any help.
TableViewController1
class MenuTypeTableViewController: UITableViewController, MenuTypeServerProtocol {
//Properties
var cellItems: NSArray = NSArray()
var selectedItem = String()
override func viewDidLoad() {
super.viewDidLoad()
let menuTypeServer = MenuTypeServer()
menuTypeServer.delegate = self
menuTypeServer.downloadItems()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier: String = "cellType"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
let item: MenuTypeModel = cellItems[indexPath.row] as! MenuTypeModel
myCell.textLabel?.text = item.type
return myCell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath)
selectedItem = (selectedCell?.textLabel?.text)!
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "typeItems" {
let destinationVC = segue.destination as? TypeItemsTableViewController
destinationVC?.selectedItem = self.selectedItem
}
}
}
TableViewController2:
class TypeItemsTableViewController: UITableViewController, TypeItemsServerProtocol {
//Properties
var cellItems: NSArray = NSArray()
var selectedItem: String = String()
let typeItemsServer = TypeItemsServer()
override func viewDidLoad() {
super.viewDidLoad()
typeItemsServer.delegate = self
self.typeItemsServer.foodType = self.selectedItem
self.typeItemsServer.downloadItems()
self.tableView.reloadData()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier: String = "cellTypeItem"
let myCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
let item: TypeItemsModel = cellItems[indexPath.row] as! TypeItemsModel
myCell.textLabel?.text = item.name!
return myCell
}
}
Try adding this to TypeItemsTableViewController
override func viewDidLoad() {
super.viewDidLoad()
cellItems = NSArray()//make sure you have the empty array at the start
typeItemsServer.delegate = self
self.typeItemsServer.foodType = self.selectedItem
self.typeItemsServer.downloadItems()
self.tableView.reloadData()
}
and
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
typeItemsServer.delegate = nil
}
Add this at the top
var cellItems: NSArray = NSArray() {
didSet {
tableview.reloadData()
}
}
Now you can remove other tableview.reloadData() calls since it will automatically be called once cellItems are set...
I think you have a timing problem. You're reloading right after your async data call. You reload but your data isn't in place at that time. Try using functions with escaping or use "didSet" on your data like:
var dataArray: [type] {
didSet {
tableview.reloadData()
}
}

Resources