Issue loading data from array to UITableView cells - ios

I am very new to swift programming and trying to build an app to take orders and relay them to an admin app. My data is not loading in my UITableView and I'm not sure why, as far as I can tell I've done everything by the book. I am loading data from a node server I created and when printing the contents of the array all items are printed as key,pair values. The UIimages are loading in each of the tableView cells but the labels are not and after setting the labels and printing them, the values are still nil of the labels.
I created a TableView class called PizzaListTableViewController and a custom TableViewCell class called PizzaTableViewCell. I have added a UIimage and three labels in the storyboard interface builder.
Structure is: ViewController > TableView > TableViewCell > Image, Labels
My main VC is connected to its ViewController.class
My TableViewCell is connected to its TableViewCell.class
I have an identifier and linked it up, as per code below
I linked all the outlets. Any help would be greatly appreciated!
I have tried to rewrite the classes, break all outlet connections and reconnect them, assign values in the method where the labels are set but no luck with anything.
class PizzaListTableViewController: UITableViewController {
var pizzas: [Pizza] = []
override func viewDidLoad() {
super.viewDidLoad()
//title you will see on the app screen at the top of the table view
navigationItem.title = "Drink Selection"
//tableView.estimatedRowHeight = 134
//tableView.rowHeight = UITableViewAutomaticDimension
fetchInventory { pizzas in
guard pizzas != nil else { return }
self.pizzas = pizzas!
//print(self.pizzas)
self.tableView.reloadData()
//print(self.pizzas)
}
} //end of viewDidLoad
private func fetchInventory(completion: #escaping ([Pizza]?) -> Void) {
Alamofire.request("http://127.0.0.1:4000/inventory", method: .get)
.validate()
.responseJSON { response in
guard response.result.isSuccess else { return completion(nil) }
guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }
let inventory = rawInventory.compactMap { pizzaDict -> Pizza? in
var data = pizzaDict!
data["image"] = UIImage(named: pizzaDict!["image"] as! String)
//print(data)
//print("CHECK")
print("Printing all data: ", Pizza(data: data))
//printing all inventory successful
return Pizza(data: data)
}
//self.tableView.reloadData()
completion(inventory)
}
}
#IBAction func ordersButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "orders", sender: nil)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
//PRINTING ROWS 0 TWICE in console
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//print("ROWS", pizzas.count)
return self.pizzas.count
}
//THIS IS WHERE THE CELL IDENTIFIER IS ??
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//print("IN CELLFORROWAT")
tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "cell")
let cell: PizzaTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PizzaTableViewCell
//cell.backgroundColor = Services.baseColor
cell.name?.text = pizzas[indexPath.row].name
cell.imageView?.image = pizzas[indexPath.row].image
cell.amount?.text = "$\(pizzas[indexPath.row].amount)"
cell.miscellaneousText?.text = pizzas[indexPath.row].description
print(cell.name?.text! as Any)
print(cell.imageView as Any)
//print("END CELLFORROWAT")
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
} //END OF
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "pizza", sender: self.pizzas[indexPath.row] as Pizza)
} //END OF override func tableView
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "pizza" {
guard let vc = segue.destination as? PizzaViewController else { return }
vc.pizza = sender as? Pizza
}
} //END OF override preppare func
}
class PizzaTableViewCell: UITableViewCell {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var pizzaImageView: UIImageView!
#IBOutlet weak var amount: UILabel!
#IBOutlet weak var miscellaneousText: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
//Configure the view for the selected state
}
}
struct Pizza {
let id: String
let name: String
let description: String
let amount: Float
let image: UIImage
init(data: [String: Any]) {
//print("CHECK:: pizza.swift")
self.id = data["id"] as! String
self.name = data["name"] as! String
// self.amount = data["amount"] as! Float
self.amount = ((data["amount"] as? NSNumber)?.floatValue)!
self.description = data["description"] as! String
self.image = data["image"] as! UIImage
}
}
I have also printed values of the array to console and the data is printing as expected but values of cell.name?.text, cell.amount?.text, and cell.miscellaneousText?.text print nil.

Please try to reload your tableview in Main thread inside the code that you pass as a parameter to fetchInventory:
DispatchQueue.main.async {
self.tableView.reloadData()
}
So, your fetchInventory call should become:
fetchInventory { pizzas in
guard pizzas != nil else { return }
self.pizzas = pizzas!
//print(self.pizzas)
DispatchQueue.main.async {
self.tableView.reloadData()
}
//print(self.pizzas)
}
Please avoid to do UI work from a background thread because it is not correct/safe. Also, you may try to set self?.pizzas too inside that main thread block.
And please take into account Alan's advice on double call.
Please remove completely the register from tableView/cellForRow.
// tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "cell")
Instead of:
cell.imageView?.image = pizzas[indexPath.row].image
put:
cell.pizzaImageView?.image = pizzas[indexPath.row].image
This is your outlet name.
Please check my test below that is working :
import UIKit
class PizzaListTableViewController: UITableViewController {
var pizzas: [Pizza] = []
override func viewDidLoad() {
super.viewDidLoad()
//title you will see on the app screen at the top of the table view
navigationItem.title = "Drink Selection"
//tableView.estimatedRowHeight = 134
//tableView.rowHeight = UITableViewAutomaticDimension
fetchInventory { pizzas in
guard pizzas != nil else { return }
self.pizzas = pizzas!
print(self.pizzas)
DispatchQueue.main.async {
self.tableView.reloadData()
}
//print(self.pizzas)
}
} //end of viewDidLoad
private func fetchInventory(completion: #escaping ([Pizza]?) -> Void) {
let rawInventory0 = [
[
"id": "1",
"name": "name1",
"amount": 1234,
"description": "description1",
"image": "image1"
],
[
"id": "2",
"name": "name2",
"amount": 1235,
"description": "description2",
"image": "image2"
],
[
"id": "3",
"name": "name3",
"amount": 1236,
"description": "description3",
"image": "image3"
],
[
"id": "4",
"name": "name4",
"amount": 1237,
"description": "description4",
"image": "image4"
]
] as? [[String: Any]?]
guard let rawInventory1 = rawInventory0 as? [[String: Any]?] else { return completion(nil) }
let inventory = rawInventory1.compactMap { pizzaDict -> Pizza? in
var data = pizzaDict!
data["image"] = UIImage(named: pizzaDict!["image"] as! String)
print(data)
print("CHECK")
print("Printing all data: ", Pizza(data: data))
//printing all inventory successful
return Pizza(data: data)
}
//self.tableView.reloadData()
completion(inventory)
}
// MARK: - Table view data source
#IBAction func ordersButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "orders", sender: nil)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
//PRINTING ROWS 0 TWICE in console
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//print("ROWS", pizzas.count)
return self.pizzas.count
}
//THIS IS WHERE THE CELL IDENTIFIER IS ??
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//print("IN CELLFORROWAT")
// tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "cell")
let cell: PizzaTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! PizzaTableViewCell
//cell.backgroundColor = Services.baseColor
cell.name?.text = pizzas[indexPath.row].name
cell.pizzaImageView?.image = pizzas[indexPath.row].image
cell.amount?.text = "\(pizzas[indexPath.row].amount)"
cell.miscellaneousText?.text = pizzas[indexPath.row].description
print(cell.name?.text! as Any)
//print(cell.imageView as Any)
//print("END CELLFORROWAT")
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
} //END OF
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "pizza", sender: self.pizzas[indexPath.row] as Pizza)
} //END OF override func tableView
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "pizza" {
guard let vc = segue.destination as? PizzaViewController else { return }
vc.pizza = sender as? Pizza
}
} //END OF override preppare func
}

Related

Issues loading data into UITableView from API call

I am new to ios development and running into issue loading data from an API call to a UITableView. The app is going to serve as ordering system. To get data into each cell of the tableView I have a button on a viewController that sends a post request with the id of the item and the id is matched to items in memory on the server. I have a view controller that has a tableView of the current ordered items which is where my problem is.
I have two other view controllers (BeerListTableViewController and CocktailListTableViewController) with tableViews in them loaded with the items that you can order. These tableViews are loading correctly with their respective items. When pressing on a cell in either tableView I perform a segue to another viewController (BeerDetailViewController or CocktailDetailViewController) where the item is shown and an "Place Order" button is shown. The place order button sends the item to the server where it is added to the array of orders and then popViewController to get back to the tableView. I have verified that the items are getting added correctly. In both BeerListTableViewController and CocktailListTableViewController I have added a barButtonItem "Orders" which segues to DrinkOrderTableView and this is where my ISSUE lies. In this tableView only the orders from BeerDetailViewController are showing. I have included code from the xcode project and my node index.js file. Any help is much appreciated!
class DrinkOrdersTableViewController: UITableViewController {
var orders: [Order] = []
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Current Orders"
fetchAllCurrentOrders { orders in
self.orders = orders!
print(self.orders)
self.tableView.reloadData()
}
}
private func fetchAllCurrentOrders(completion: #escaping([Order]?) -> Void) {
Alamofire.request("http://127.0.0.1:4000/orders", method: .get)
.validate()
.responseJSON { response in
guard response.result.isSuccess else { return completion(nil) }
guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }
let currentOrders = rawInventory.compactMap { ordersDict -> Order? in
guard let orderId = ordersDict!["id"] as? String,
let orderStatus = ordersDict!["status"] as? String,
var pizza = ordersDict!["pizza"] as? [String: Any] else { return nil }
pizza["image"] = UIImage(named: pizza["image"] as! String)
return Order(
id: orderId,
pizza: Pizza(data: pizza),
status: OrderStatus(rawValue: orderStatus)!
)
}
completion(currentOrders)
}
}
#IBAction func closeButtonPressed(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Debugging ROWS", orders.count)
return orders.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "order", for: indexPath)
let order = orders[indexPath.row]
cell.textLabel?.text = order.pizza.name
cell.imageView?.image = order.pizza.image
cell.detailTextLabel?.text = "$\(order.pizza.amount) - \(order.status.rawValue)"
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "orderSegue", sender: orders[indexPath.row] as Order)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "orderSegue" {
guard let vc = segue.destination as? OrderViewController else { return }
vc.order = sender as? Order
}
}
}
link to server code
Link to screencap of issue

Error setting UILabel text in custom UITableViewCell

I am quite new to Swift programming, but I am having trouble setting UILabel text in my UITableView class for individual UITableViewCell instances.
I have created a custom subclass of UITableViewCell called PizzaTableViewCell and a custom UITableView class called PizzaListTableViewController. I am trying to populate the UITableView instance with data from an array, which is being populated from an API call to my node.js server.
I have included my UITableView subclass, custom UITablveViewCell class, the struct for the data, and a link to a screenshot of the Simulator loading what I have done. Any help is greatly appreciated!
I have verified that the data is being put in the array with no issues, as I can print the contents after the call to fetchInventory method. I have been able to set a single textLabel with
cell.textLabel?.text = pizzas[indexPath.row].name
along with an image in the array with:
cell.imageView?.image = pizzas[indexPath.row].image
but I have 2 more labels that I need in each cell which I cannot set. I have checked my IBOutlets and Storyboard identifiers, and they match the code.
class PizzaListTableViewController: UITableViewController {
var pizzas: [Pizza] = []
override func viewDidLoad() {
super.viewDidLoad()
//title you will see on the app screen at the top of the table view
navigationItem.title = "Drink Selection"
tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
//tableView.estimatedRowHeight = 134
//tableView.rowHeight = UITableViewAutomaticDimension
fetchInventory { pizzas in
guard pizzas != nil else { return }
self.pizzas = pizzas!
print(self.pizzas)
//self.tableView.reloadData()
//print(self.pizzas)
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
}
} //end of viewDidLoad
private func fetchInventory(completion: #escaping ([Pizza]?) -> Void) {
Alamofire.request("http://127.0.0.1:4000/inventory", method: .get)
.validate()
.responseJSON { response in
guard response.result.isSuccess else { return completion(nil) }
guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }
let inventory = rawInventory.compactMap { pizzaDict -> Pizza? in
var data = pizzaDict!
data["image"] = UIImage(named: pizzaDict!["image"] as! String)
//print(data)
//print("CHECK")
print("Printing each item: ", Pizza(data: data))
//printing all inventory successful
return Pizza(data: data)
}
completion(inventory)
}
}
#IBAction func ordersButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "orders", sender: nil)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
//PRINTING ROWS 0 TWICE in console
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("ROWS", pizzas.count)
return self.pizzas.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: PizzaTableViewCell = tableView.dequeueReusableCell(withIdentifier: "Pizza", for: indexPath) as! PizzaTableViewCell
//cell.backgroundColor = Services.baseColor
//cell.pizzaImageView?.image = pizzas[indexPath.row].image
//THESE WORK BUT ARE A STATIC WAY OF SETTING THE CELLS
//CAN ONLY SET THE SELL WITH A SINGLE TEXT LABEL FROM THE DATA ARRAY
cell.imageView?.image = pizzas[indexPath.row].image
cell.textLabel?.text = pizzas[indexPath.row].name
//cell.textLabel?.text = pizzas[indexPath.row].description
//cell.textLabel?.text = "$\(pizzas[indexPath.row].amount)"
// cell.name?.text = pizzas[indexPath.row].name
// cell.imageView?.image = pizzas[indexPath.row].image
// cell.amount?.text = "$\(pizzas[indexPath.row].amount)"
// cell.miscellaneousText?.text = pizzas[indexPath.row].description
//print(cell.name?.text! as Any)
print(cell.imageView as Any)
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
} //END OF
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "pizzaSegue", sender: self.pizzas[indexPath.row] as Pizza)
} //END OF override func tableView
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "pizzaSegue" {
guard let vc = segue.destination as? PizzaViewController else { return }
vc.pizza = sender as? Pizza
}
} //END OF override preppare func
}
class PizzaListTableViewController: UITableViewController {
var pizzas: [Pizza] = []
override func viewDidLoad() {
super.viewDidLoad()
//title you will see on the app screen at the top of the table view
navigationItem.title = "Drink Selection"
tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
//tableView.estimatedRowHeight = 134
//tableView.rowHeight = UITableViewAutomaticDimension
fetchInventory { pizzas in
guard pizzas != nil else { return }
self.pizzas = pizzas!
print(self.pizzas)
//self.tableView.reloadData()
//print(self.pizzas)
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
}
} //end of viewDidLoad
private func fetchInventory(completion: #escaping ([Pizza]?) -> Void) {
Alamofire.request("http://127.0.0.1:4000/inventory", method: .get)
.validate()
.responseJSON { response in
guard response.result.isSuccess else { return completion(nil) }
guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }
let inventory = rawInventory.compactMap { pizzaDict -> Pizza? in
var data = pizzaDict!
data["image"] = UIImage(named: pizzaDict!["image"] as! String)
//print(data)
//print("CHECK")
print("Printing each item: ", Pizza(data: data))
//printing all inventory successful
return Pizza(data: data)
}
completion(inventory)
}
}
#IBAction func ordersButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "orders", sender: nil)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
//PRINTING ROWS 0 TWICE in console
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("ROWS", pizzas.count)
return self.pizzas.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: PizzaTableViewCell = tableView.dequeueReusableCell(withIdentifier: "Pizza", for: indexPath) as! PizzaTableViewCell
//cell.backgroundColor = Services.baseColor
//cell.pizzaImageView?.image = pizzas[indexPath.row].image
//THESE WORK BUT ARE A STATIC WAY OF SETTING THE CELLS
//CAN ONLY SET THE SELL WITH A SINGLE TEXT LABEL FROM THE DATA ARRAY
cell.imageView?.image = pizzas[indexPath.row].image
cell.textLabel?.text = pizzas[indexPath.row].name
//cell.textLabel?.text = pizzas[indexPath.row].description
//cell.textLabel?.text = "$\(pizzas[indexPath.row].amount)"
// cell.name?.text = pizzas[indexPath.row].name
// cell.imageView?.image = pizzas[indexPath.row].image
// cell.amount?.text = "$\(pizzas[indexPath.row].amount)"
// cell.miscellaneousText?.text = pizzas[indexPath.row].description
//print(cell.name?.text! as Any)
print(cell.imageView as Any)
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
} //END OF
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "pizzaSegue", sender: self.pizzas[indexPath.row] as Pizza)
} //END OF override func tableView
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "pizzaSegue" {
guard let vc = segue.destination as? PizzaViewController else { return }
vc.pizza = sender as? Pizza
}
} //END OF override preppare func
}
struct Pizza {
let id: String
let name: String
let description: String
let amount: Float
//let amount: String
let image: UIImage
init(data: [String: Any]) {
//print("CHECK:: pizza.swift")
self.id = data["id"] as! String
self.name = data["name"] as! String
// self.amount = data["amount"] as! Float
self.amount = ((data["amount"] as? NSNumber)?.floatValue)!
self.description = data["description"] as! String
self.image = data["image"] as! UIImage
}
}
As noted above, I have been able to print the contents of the data array with beer names, pictures, descriptions and etc. I have tried to print to console
print(cell.name?.text)
after setting
cell.name?.text = pizzas[indexPath.row].name
but it prints nil and this is a problem. I have been stuck with this for about 2 weeks!
IBOutlets screenshot:
I think i found your Problem, let me explain
What you are doing here is you have a custom UITableViewCell defined in the Storyboard in a Controller named "Root View Controller" which is not your PizzaListTableViewController to put it simply
And as you said you have absolutely no issue regarding the IBOutlets
Now when you say
tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
In Your PizzaListTableViewController you are not linking it with the UI of the cell rather just the Code (This is only used when there is no xib of the cell)
Now what you can do to solve this
Solution # 1
Move/Copy your UI of the PizzaTableViewCell to PizzaListTableViewController in the storyboard from your "Root View Controller"
Make sure you add a Reuse Identifier in the Attribute Inspector of the cell in the storyboard
remove tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza") this wont give you an error this time as it will automatically get register
Make sure all the IBOutlets are connected
Solution # 2
create a separate Nib (xib) of the cell
and now you have to register the cell here like
tableView.register(UINib(nibName: "PizzaTableViewCell", bundle: Bundle.main), forCellReuseIdentifier: "PizzaCell")
Hope this helps.
Try this
cell.name?.text = ...
cell.amount?.text = ...
cell.miscellaneousText?.text = ...
cell.pizzaImageView?.image = ...
If it still does not work then make sure your cell and your outlets are not null when setting its value. Hope it helps !
There is something definitely strange going on with your setup.
If you try to name the IBOutlets with the same name as the UITableViewCell default property it'll throw an error. The fact that you were able to set those names and build successfully is strange.
From the screenshot above you can see what happens when I attempted to do this.
Make sure your Table View Controller class is set in the storyboard.
Make sure your Table View Cell class is set in the storyboard.
Make sure that all your outlets are properly connected.
Make sure your Table View Cell Identifier is provided in the storyboard.
My Table View Controller Subclass
My Table View Cell Subclass
cell.imageView?.image and cell.textLabel?.text are optional properties of the table view itself. They are not the properties of the custom cell that you designed.
You use tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza") when you have designed a table view cell in XIB. But as you have designed the cell in the storyboard itself you should set the cell reuse identifier and cell class in the storyboard.
I hope this will help you out.

How to handle tableview cell clicks and segues when delegate and datasource class is separate?

I tried to separate TableViewDelegate and TableViewDataSource to a separate class from ViewController and I'm facing a couple of problems now.
First problem:
App loads the tableview with all content but when I click on it or try to scroll all data disappears.
Second problem:
On click cell should link to another view where is more content displayed. I push data to this view using function. But when I separated the delegate and datasource to other class it doesnt work.
prepare(for segue: UIStoryboardSegue, sender: Any?)
Here is my code for view controller:
import UIKit
import Foundation
import os
class FirstViewController: UIViewController {
#IBOutlet weak var tableview: UITableView!
#IBOutlet weak var offlineModePicture: UIBarButtonItem!
#IBOutlet weak var refresh_button: UIBarButtonItem!
var wyznania_page = 0 // page
var isNewDataLoading = false
var wyznania = [[WyznanieData](),[WyznanieData](),[WyznanieData](),[WyznanieData](),[WyznanieData]()]
let activitiyViewController = ActivityViewController(message: "Ładowanie...😇")
override func viewDidLoad() {
super.viewDidLoad()
wyznania[wyznania_page].append(WyznanieData(date: "date", story: "story", sharedLink: "link", tag: "asd", fav: false, page: 1)!)
wyznania[wyznania_page].append(WyznanieData(date: "date", story: "story", sharedLink: "link", tag: "asd", fav: false, page: 1)!)
wyznania[wyznania_page].append(WyznanieData(date: "date", story: "story", sharedLink: "link", tag: "asd", fav: false, page: 1)!)
self.navigationController?.navigationBar.sizeToFit()
view.backgroundColor = UIColor.black
tabBarController?.tabBar.barTintColor = ColorsUI.bar
tabBarController?.tabBar.tintColor = UIColor.white
navigationController?.navigationBar.barTintColor = ColorsUI.bar
navigationController?.navigationBar.tintColor = UIColor.white
let customDelegate = TableViewDelegate(dataForRows: wyznania[wyznania_page])
self.tableview.delegate = customDelegate
self.tableview.dataSource = customDelegate
}
override internal var preferredStatusBarStyle : UIStatusBarStyle {
return .lightContent
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
switch(segue.identifier ?? "") {
case "ShowDetail":
guard let storyDetailViewController = segue.destination as? WyznanieViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
guard let selectedStopCell = sender as? Wyznanie else {
fatalError("Unexpected sender: \(String(describing: sender))")
}
guard let indexPath = tableview.indexPath(for: selectedStopCell) else {
fatalError("The selected cell is not being displayed by the table")
}
let selectedStory = wyznania[wyznania_page][(indexPath as NSIndexPath).row]
storyDetailViewController.wyznanie = selectedStory
default:
fatalError("Unexpected Segue Identifier; \(String(describing: segue.identifier))")
}
}
#IBAction func unwindToList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? WyznanieViewController, let story = sourceViewController.wyznanie {
if let selectedIndexPath = tableview.indexPathForSelectedRow {
// Update an existing story.
print("updating")
wyznania[wyznania_page][selectedIndexPath.row] = story
tableview.reloadRows(at: [selectedIndexPath], with: .none)
}
else {
// Add a new story
print("adding new")
}
}
}
}
And my Delegate and DataSource class:
[import UIKit
class TableViewDelegate: NSObject,UITableViewDelegate,UITableViewDataSource {
var wyznania = \[WyznanieData\]()
init(dataForRows: \[WyznanieData\]) {
self.wyznania = dataForRows
super.init()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return wyznania.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "story_cell", for:indexPath) as? Wyznanie else {
fatalError("The dequeued cell is not an instance of WyznanieTableViewCell.")
}
let wyznanie = wyznania\[indexPath.row\]
cell.date.text = wyznanie.date
cell.story.text = wyznanie.story
cell.story.setContentOffset(CGPoint.zero, animated: true)
cell.story.textColor = UIColor.white
cell.backgroundColor = ColorsUI.cell_backgroung
cell.layer.borderWidth = 3.0
cell.layer.borderColor = ColorsUI.cell_borderColor
return cell
}
}]
1
[]
Try making your delegate variable global. it must be deallocation when goes out of scope in viewDidLoad (dataSource and delegate are weak in UITableView).
Extract following declaration global.
var customDelegate: TableViewDelegate!
then in viewDidLoad do following
customDelegate = TableViewDelegate(dataForRows: wyznania[wyznania_page])

Swift 3 - Setting variable in TableViewController swift file depending on cell clicked

I'm trying to set the a string depending on which cell in a tableView is clicked. The BlueLineTableViewController is the one which should capture the user's click.
import UIKit
class BlueLineTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return bluelinestations.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "bluelinecell", for: indexPath)
let station = bluelinestations[indexPath.row]
cell.textLabel?.text = station.name
cell.imageView?.image = UIImage(named: station.image)
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let row = indexPath.row
if row == 0 {
BlueBelmontTableViewController().feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40890&outputType=JSON"
}
if row == 1 {
BlueBelmontTableViewController().feed="http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40820&outputType=JSON"
}
}
The BlueBelmontTableViewController's feed variable should change/be set to another url depending on which cell is clicked in the BlueLineTableViewController.
import UIKit
class BlueBelmontTableViewController: UITableViewController {
class Destinations {
var destination: String = ""
var time: String = ""
}
var feed = ""
var dataAvailable = false
var records = [Destinations]()
override func viewDidLoad() {
super.viewDidLoad()
parseData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
for r in records {
r.time = ""
r.destination = ""
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataAvailable ? records.count : 15
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (dataAvailable) {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let destinationRow = records[indexPath.row]
cell.textLabel?.text = destinationRow.destination
cell.detailTextLabel?.text = destinationRow.time
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "PlaceholderCell", for: indexPath)
return cell
}
}
func parseData() {
guard let feedURL = URL(string: feed) else {
return
}
let request = URLRequest(url: feedURL)
let task = URLSession.shared.dataTask(with: request) {(data, response, error) in
if error != nil
{
print("Error")
}
else {
if let content = data {
do {
let json = try JSONSerialization.jsonObject(with: content, options: []) as? [String:Any] ?? [:]
print(json)
if let ctattimetable = json["ctatt"] as? [String:Any] {
if let estArrivalTime = ctattimetable["eta"] as? [[String:Any]] {
for item in estArrivalTime{
if let headingTowards = item["destNm"] as? String,
let arrivalTime = item["arrT"] as? String {
let record = Destinations()
record.destination = headingTowards
record.time = arrivalTime
self.records.append(record)
}
self.dataAvailable = true
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}
catch {
}
}
}
}
task.resume()
}
}
I've tried setting the url in the didSelectRowAt method depending on the indexPath.row as can be seen in BlueLineTableViewController, but it does not seem to do anything. Does anybody know how I would go about doing this?
Below is the Main.storyboard of this part of my project:
Your are not able to pass value because you are setting feed property to the completely new instance of BlueBelmontTableViewController not the one that is added in navigation stack using your segue that you have created from your UITableViewCell to BlueBelmontTableViewController.
What you need to do is override prepareForSegue in your BlueLineTableViewController to pass your value to BlueBelmontTableViewController.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! BlueBelmontTableViewController
if let indexPath = self.tableView.indexPathForSelectedRow {
if indexPath.row == 0 {
vc.feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40890&outputType=JSON"
}
if indexPath.row == 1 {
vc.feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40820&outputType=JSON"
}
}
}
instead of
BlueBelmontTableViewController().feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40890&outputType=JSON"
use
self.feed = "http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=mykey&mapid=40890&outputType=JSON"
beacause BlueBelmontTableViewController() is initialing new instance of BlueBelmontTableViewController and you want to change the instance you already have so you should use self instead of creating new instance.

UitableView selected cell to the other view

I have a UITableView where data is loaded from a database, a JSON. How do I get this when I select a line, which is taken in another view?
The automarke is to be selected in the tableview and displayed in the label of the other view.
class AutoMarkeTableView: UITableViewController {
var items = [[String:AnyObject]]()
#IBOutlet var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "URL_LINK")!
let urlSession = URLSession.shared
let task = urlSession.dataTask(with: url) { (data, response, error) in
// JSON parsen und Ergebnis in eine Liste von assoziativen Arrays wandeln
let jsonData = try! JSONSerialization.jsonObject(with: data!, options: [])
self.items = jsonData as! [[String:AnyObject]]
// UI-Darstellung aktualisieren
OperationQueue.main.addOperation {
self.tableView.reloadData()
}
}
task.resume()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "markeCell", for: indexPath)
let item = items[indexPath.row]
cell.textLabel?.text = item["makename"] as? String
return cell
}
}
class FahrzeugAngabenView: UIViewController {
#IBOutlet weak var itemMarkeLabel: UILabel!
}
You could temporarily save the selected item in a variable. Something like this:
var selectedItem: Item?
func tableView(tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedItem = items[indexPath.row]
self.performSegue(withIdentifier: "auto", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "auto" {
let destVc = segue.destination as! FahrzeugAngabenView
destVc.selectedItemName = selectedItem.title
selectedItem = nil
}
}
Not the most elegant solution, but i would expect this to work.

Resources