Remove row from table view - ios

How can I remove row from tableview cell I cant remove the cell from table view I don't know why and when I try to delete never gone from table view please someone tell me how I can delete it one cell from multiple cell I have it. I know I should have var but I did not put it for example var = something
class addCartTblVC: UITableViewController {
//MARK: -Variables
var coreDataVariables = GetCoreDataVariables()
//MARK: -ViewMethords
override func viewDidLoad() {
super.viewDidLoad()
self.fetchCart()
let backItem = UIBarButtonItem()
backItem.title = "Back"
navigationItem.backBarButtonItem = backItem
tableView.tableFooterView = UIView()
}
//MARK: -tableView dataSource
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return coreDataVariables.result_add_Cart.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cartCell = tableView.dequeueReusableCell(withIdentifier: "cartCell", for: indexPath) as! AddCartTblCell
let img = coreDataVariables.result_add_Cart[indexPath.row].product_Image!
let userPhotoString = img
let imageUrl:URL = URL(string: userPhotoString)!
let imageData:Data = try! Data(contentsOf: imageUrl)
// Add photo to a cell as a subview
if let image = UIImage(data: imageData){
cartCell.pro_Img.image = image
}
cartCell.pro_MRP.text = coreDataVariables.result_add_Cart[indexPath.row].product_Price
cartCell.pro_Details.text = coreDataVariables.result_add_Cart[indexPath.row].product_Name
cartCell.pro_Discount.text = coreDataVariables.result_add_Cart[indexPath.row].product_Discount
if cartCell.pro_Discount.text! == cartCell.pro_MRP.text!{
cartCell.pro_Discount.isHidden = true
cartCell.crossView.isHidden = true
}
else{
cartCell.pro_MRP.textColor = UIColor.darkGray
cartCell.pro_Discount.isHidden = false
cartCell.crossView.isHidden = false
}
cartCell.proceedBtn.tag = indexPath.row
cartCell.proceedBtn.addTarget(self, action: #selector(addCartTblVC.ProceedTapped(_:)), for: .touchUpInside)
cartCell.removeFromCoreDataBtn.tag = indexPath.row
cartCell.removeFromCoreDataBtn.addTarget(self, action: #selector(addCartTblVC.deleteFromCart(_:)), for: .touchUpInside)
return cartCell
}
//MARK: -fetchFunction
func fetchCart(){
let cart = Add_Cart()
coreDataVariables.result_add_Cart = cart.fetchCart()!
}
//MARK: -Proceed Action
#IBAction func back(_ sender: UIBarButtonItem) {
dismiss(animated: false, completion: nil)
}

Before loading table data try to loop on coreDataVariables.result_add_Cart and check for duplicate elements and try to make them single element by multiplying product_Price * n of duplicates and take care of product_Discount also.
your approach should be like this ((this method only for removing duplicates)) :
extension Array where Element: Equatable {
mutating func removeDuplicates() {
var result = [Element]()
for value in self {
if !result.contains(value) {
result.append(value)
}
}
self = result
}
==> Hope not getting negative vote for this answer :D

Related

Is there a way to get the id of a UITableViewCell?

my problem: I want to open some kind of Profil if a user pushes a Button in a Table-View Cell. The Cells Data is downloaded from Parse.
The idea is based on Instagram, if you click on the username-button on Insta the profile from the user who posted the image will open. I want to create the same code, but i can't create the code to get the user. Can you help me?
Heres some code:
import UIKit
import Parse
class HomeController: UIViewController, UITableViewDelegate, UITableViewDataSource {
private let reuseIdentifer = "FeedCell"
var delegate: HomeControllerDelegate?
var newCenterController: UIViewController!
let tableView = UITableView()
//Für Parse:
var users = [String: String]()
var comments = [String]()
var usernames = [String]()
var lastnames = [String]()
var imageFiles = [PFFileObject]()
var wischen: UISwipeGestureRecognizer!
var wischen2: UISwipeGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
getData()
configureNavigationBar()
configurateTableView()
wischen = UISwipeGestureRecognizer()
wischen.addTarget(self, action: #selector(handleMenuToggle))
wischen.direction = .right
wischen.numberOfTouchesRequired = 1
view.addGestureRecognizer(wischen)
wischen2 = UISwipeGestureRecognizer()
wischen2.addTarget(self, action: #selector(handleMenuToggle))
wischen2.direction = .left
wischen2.numberOfTouchesRequired = 1
view.addGestureRecognizer(wischen2)
}
#objc func handleMenuToggle() {
delegate?.handleMenuToggle(forMenuOption: nil)
}
#objc func showProfile() {
let vc: AProfileViewController!
vc = AProfileViewController()
vc.modalPresentationStyle = .fullScreen
present(vc, animated: true)
}
func configureNavigationBar() {
navigationController?.navigationBar.barTintColor = .darkGray
navigationController?.navigationBar.barStyle = .black
navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.font: UIFont(name: "Noteworthy", size: 22)!, NSAttributedString.Key.foregroundColor: UIColor.white]
//navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
navigationItem.title = "Mobile Job Board"
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "ic_menu_white_3x").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleMenuToggle))
navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "ic_mail_outline_white_2x").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(showCreateNewArticle))
}
//MARK: Table View
//skiped table view configuration
}
// - MARK: Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return comments.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifer, for: indexPath) as! FeedCell
imageFiles[indexPath.row].getDataInBackground { (data, error) in
if let imageData = data {
if let imageToDisplay = UIImage(data: imageData) {
cell.postImage.image = imageToDisplay
}
}
}
cell.descriptionLabel.text = comments[indexPath.row]
cell.userButton.setTitle("\(usernames[indexPath.row]) \(lastnames[indexPath.row])", for: UIControl.State.normal)
cell.userButton.addTarget(self, action: #selector(showProfile), for: .touchUpInside)
return cell
}
//skiped
}
Thanks a lot!
Tom
The issue here is that your button works on a selector and it has no idea about the sender or where it was called from.
I would do this by creating a custom table view cell (e.g. FeedCell) which allows you to set a delegate (e.g. FeedCellDelegate). Set your class as the delegate for the cell and pass into the cell it's current indexPath. You can then return the indexPath in the delegate call.
Example: Note that code has been removed for simplicity and this code has not been tested. This is simply to guide you in the right direction.
View Controller
import UIKit
class HomeController: UIViewController {
// stripped additional information for example
func showProfile(_ username: String) {
let vc: AProfileViewController!
vc = AProfileViewController()
vc.username = username
vc.modalPresentationStyle = .fullScreen
present(vc, animated: true)
}
}
extension HomeController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comments.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifer, for: indexPath) as! FeedCell
cell.delegate = self
cell.descriptionLabel.text = comments[indexPath.row]
cell.userButton.setTitle("\(usernames[indexPath.row]) \(lastnames[indexPath.row])", for: UIControl.State.normal)
cell.setIndex(indexPath)
return cell
}
}
extension HomeController: FeedCellDelegate {
func didPressButton(_ indexPath: IndexPath) {
let userName = usernames[indexPath.row]
showProfile(username)
}
}
Feed Cell
import UIKit
protocol FeedCellDelegate {
didPressButton(_ indexPath: IndexPath)
}
class FeedCell: UICollectionViewCell {
var delegate: FeedCellDelegate?
var indexPath: IndexPath
#IBOutlet weak var userButton: UIButton
setIndex(_ indexPath: IndexPath) {
self.indexPath = indexPath
}
#IBAction userButtonPressed() {
if(delegate != nil) {
delegate?.didPressButton(indexPath)
}
}
}
You can generically and in a type safe way get the parent responder of any responder with:
extension UIResponder {
func firstParent<T: UIResponder>(ofType type: T.Type ) -> T? {
return next as? T ?? next.flatMap { $0.firstParent(ofType: type) }
}
}
So:
Get the parent tableviewCell of your button in the target action function
Ask your tableview for the index path
Use the index path.row to index into your users array:
#objc func showProfile(_ sender: UIButton) {
guard let cell = firstParent(ofType: UITableViewCell.self),
let indexPath = tableView.indexPath(for: cell) else {
return
}
let user = users[indexPath.row]
... do other stuff here ...
}

Swift 5 - make tableview wait until data from api call comes back (using multiple tableviews)

Issue: Fatal error when View is loading. I know the problem is because there is no data available when to table view is trying to load. But, because I am using multiple TableViews in one UI I have to force the cell return in CellForRowAt.
Is there a better way of setting up different data for each TableView?
THANKS FOR THE HELP!
import UIKit
class NewCustomTaskVC: UIViewController {
#IBAction func CancelPressed (_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBOutlet weak var taskTypeSelectionBtn: UIButton!
#IBOutlet weak var FrameSelectionBtn: UIButton!
#IBOutlet weak var AssignSelectionBtn: UIButton!
#IBAction func SelecttaskTypePressed(_ sender: Any) {
if tableViewTaskType.isHidden {
self.tableViewTaskType.isHidden = false
self.tableViewTaskType.rowHeight = 43.5
} else {
self.tableViewTaskType.isHidden = true
}
}
#IBAction func SelectFramePressed(_ sender: Any) {
if tableViewFrame.isHidden {
self.tableViewFrame.isHidden = false
} else {
self.tableViewFrame.isHidden = true
}
}
#IBAction func SelectAssignToPressed(_ sender: Any) {
if tableViewAssignTo.isHidden {
self.tableViewAssignTo.isHidden = false
} else {
self.tableViewAssignTo.isHidden = true
}
}
#IBOutlet weak var tableViewTaskType: UITableView!
#IBOutlet weak var tableViewFrame: UITableView!
#IBOutlet weak var tableViewAssignTo: UITableView!
var cellID = ""
var array = ["String", "Test", "Next","Test 2", "Test 3"]
override func viewDidLoad() {
super.viewDidLoad()
getData()
tableViewTaskType.isHidden = true
tableViewFrame.isHidden = true
tableViewAssignTo.isHidden = true
tableViewTaskType.delegate = self
tableViewFrame.delegate = self
tableViewAssignTo.delegate = self
tableViewTaskType.dataSource = self
tableViewFrame.dataSource = self
tableViewAssignTo.dataSource = self
self.tableViewTaskType.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
self.tableViewFrame.register(UITableViewCell.self, forCellReuseIdentifier: "cell2")
self.tableViewAssignTo.register(UITableViewCell.self, forCellReuseIdentifier: "cell3")
}
func getData () {
//dispatchGroup.enter()
var count = 0
APICallBack.getFramesData(completion: { success in
if success == true {
print("frames success")
count += 1
} })
APICallBack.getTaskTypeData { success in
if success == true {
print("task success")
count += 1
}
}
APICallBack.GETUserData(completion: { success in
if success == true {
print("user success")
count += 1
} })
if count == 3{
DispatchQueue.main.async {
self.tableViewTaskType.reloadData()
self.tableViewAssignTo.reloadData()
self.tableViewFrame.reloadData()
print("ALL COMPLETE")
}
}
}
}
extension NewCustomTaskVC : UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count = 1
switch tableView {
case tableViewTaskType:
count = TaskTypeData.typeModel.count
case tableViewFrame:
count = FramesData.framesModel.count
case tableViewAssignTo:
count = CustomerData.customerModel.count
default:
print("none")
return count
}
//return 5
return count
}
**PROBLEM IS HERE
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell?
if tableView == self.tableViewTaskType{
cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath)
cell!.textLabel!.text = TaskTypeData.typeModel[indexPath.row].TaskTypeName
// cell!.textLabel?.text = array[indexPath.row]
}
if tableView == tableViewFrame{
cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath)
cell!.textLabel!.text = FramesData.framesModel[indexPath.row].FrameName
// cell!.textLabel?.text = array[indexPath.row]
}
if tableView == self.tableViewAssignTo {
cell = tableView.dequeueReusableCell(withIdentifier: "cell3", for: indexPath)
cell!.textLabel!.text = UserData.userModel[indexPath.row].UserFirst
// cell.textLabel?.text = array[indexPath.row]
}
// let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath)
// cell.textLabel?.text = array[indexPath.row]
return cell!
}
** TO HERE!
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = array[indexPath.row]
print(item)
tableViewTaskType.isHidden = true
}
}
My UI View:
The error I get if I don't force it:
I assume that you want to display an empty "placeholder" cell if the data source array is empty. You need to check explicitly for that condition in your cellForRow function.
As far as co-ordinating the fetch from multiple API endpoints, you can use a DispatchGroup - Some commented code indicates you may have tried this.
override func viewDidLoad() {
super.viewDidLoad()
tableViewTaskType.isHidden = true
tableViewFrame.isHidden = true
tableViewAssignTo.isHidden = true
tableViewTaskType.delegate = self
tableViewFrame.delegate = self
tableViewAssignTo.delegate = self
tableViewTaskType.dataSource = self
tableViewFrame.dataSource = self
tableViewAssignTo.dataSource = self
self.tableViewTaskType.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
self.tableViewFrame.register(UITableViewCell.self, forCellReuseIdentifier: "cell2")
self.tableViewAssignTo.register(UITableViewCell.self, forCellReuseIdentifier: "cell3")
getData()
}
func getData () {
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
APICallBack.getFramesData(completion: { success in
if success == true {
print("frames success")
}
dispatchGroup.leave()
})
APICallBack.getTaskTypeData { success in
if success == true {
print("task success")
}
dispatchGroup.leave()
}
APICallBack.GETUserData(completion: { success in
if success == true {
print("user success")
}
dispatchGroup.leave()
})
dispatchGroup.notify {
self.tableViewTaskType.reloadData()
self.tableViewAssignTo.reloadData()
self.tableViewFrame.reloadData()
print("ALL COMPLETE")
}
}
extension NewCustomTaskVC : UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch tableView {
case tableViewTaskType:
return max(1,TaskTypeData.typeModel.count)
case tableViewFrame:
return max(1,FramesData.framesModel.count)
case tableViewAssignTo:
return max(1,CustomerData.customerModel.count)
default:
fatalError("Unexpected table view")
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch tableView {
case self.tableViewTaskType:
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath)
if !TaskTypeData.typeModel.isEmpty {
cell.textLabel!.text = TaskTypeData.typeModel[indexPath.row].TaskTypeName
}
return cell
case tableViewFrame:
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath)
if !FramesData.framesModel.isEmpty {
cell!.textLabel!.text = FramesData.framesModel[indexPath.row].FrameName
}
return cell
case self.tableViewAssignTo:
let cell = tableView.dequeueReusableCell(withIdentifier: "cell3", for: indexPath)
if !UserData.userModel.isEmpty {
cell!.textLabel!.text = UserData.userModel[indexPath.row].UserFirst
}
return cell
default:
fatalError("Unexpected Tableview")
}
}
you can set tableView.dataSource & tableView.delegate to self when your data is back
There is multiple problems with your code.
1) You call get data before your table view had registered it cells.
So if your API would load data immediately table view will be call dataSource methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
but since the
self.tableViewTaskType.register(UITableViewCell.self, forCellReuseIdentifier: "cell1")
self.tableViewFrame.register(UITableViewCell.self, forCellReuseIdentifier: "cell2")
self.tableViewAssignTo.register(UITableViewCell.self, forCellReuseIdentifier: "cell3")
called in the end of viewDidLoad, your would get crash when you dequeue your cells in cellForAtIndexPath method.
The solution is to move getData call to the end of viewDidLoad method.
2) If you want to display all tables data at one time (when API is complete loading getFramesData, getTaskTypeData and GETUserData) you would need to synchronise this callbacks. You could do this with DispatchGroup.
func getData () {
let apiDispatchGroup = DispatchGroup()
APICallBack.getFramesData { success in
apiDispatchGroup.leave()
}
apiDispatchGroup.enter()
APICallBack.getTaskTypeData { success in
apiDispatchGroup.leave()
}
apiDispatchGroup.enter()
APICallBack.GETUserData { success in
apiDispatchGroup.leave()
}
apiDispatchGroup.enter()
apiDispatchGroup.notify(queue: DispatchQueue.main) {
self.tableViewTaskType.reloadData()
self.tableViewAssignTo.reloadData()
self.tableViewFrame.reloadData()
}
}
3) It's not good idea to use one dataSOurce class for mutltiple UITableView, because of dataSource become god object. The better approach is to use one ContainerViewController that contains three child UITableViewController, and pass data to the childs when data has loaded from the API.
It is perfectly fine to have multiple view controllers in one screen. So I suggest that you create three view controllers, one for each table view. And each table view has its own datasource. Then use a custom container view controller as described here: https://developer.apple.com/documentation/uikit/view_controllers/creating_a_custom_container_view_controller

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.

UISwitch state in Tableviewcell resets when user scrolls - Swift

I've searched for a solutions on this issue but none seem to work for my use case.
I have a table inside a viewcontroller and the issue I am facing is that when scrolling the UISwitch state is reset to OFF. I understand table cells are reused, but how do I implement a solution that will restore the state of UISwitch when a user scrolls based on my code below
import UIKit
class StirrViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var mylabel: UILabel!
var myString = String()
#IBAction func stirrBtn(_ sender: AnyObject) {
}
var timeSelected = String()
var selectedTimeArr = [String]()
override func viewDidLoad() {
super.viewDidLoad()
mylabel.text = myString
self.timeSelected = myString
}
func switchChanged(_ sender : UISwitch!){
print("table row switch Changed \(sender.tag)")
print("The switch is \(sender.isOn ? "ON" : "OFF")")
let kValue = (sender.tag + 1)
let keyValue = String(kValue)
if sender.isOn {
recipeSettings.boolStirrSwitch[keyValue] = true
recipeSettings.switchedOnArr.append(keyValue)
} else {
recipeSettings.boolStirrSwitch[keyValue] = false
}
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
let stringNum = Int(self.timeSelected)
recipeSettings.recipeTimeSet2 = stringNum!
return(stringNum)!
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UITableViewCell
//here is programatically switch make to the table view
let switchView = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
switchView.tag = indexPath.row // for detect which row switch Changed
switchView.addTarget(self, action: #selector(self.switchChanged(_:)), for: .valueChanged)
cell.accessoryView = switchView
// Process data displayed in rows(minutes)
let endTime = Int(self.timeSelected)
let startTime = Int(1)
// Recipe time array
let timeArray: [Int] = Array(startTime...endTime!)
let stringTimeArr = timeArray.map{String($0)}
// Save time array to global variable
recipeSettings.recipeTimeSetArr = stringTimeArr
// Create a boolean Array to hold all default false booleans
let defBool: Bool = false
var defBoolArr: [Bool] = []
// Fill the array with the defaults boolean
for _ in 0..<stringTimeArr.count{defBoolArr.append(defBool)}
// Map the array to global dictionary containing the Time in an array and default "false" value
for i in 0..<stringTimeArr.count {
recipeSettings.boolStirrSwitch[stringTimeArr[i]] = defBoolArr[i]
}
// Add the minutes to cell table
cell.textLabel?.text = stringTimeArr[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
As you can see in my code I do save the state of each UI switch in a global variable dictionary. How can I solve the issue of UISwitch changing states based on this code? All help is appreciated. Thanks in advance
var switchState = [String : Bool]()
your recipeSettings.boolStirrSwitch should be decleard like that.
As you are using timeSelected as numberOfRowsInSection as showing
your cell.textLabel from that so you don't need extra stringTimeArr
for that.
All the processing you do in cellForRowAt it will happen again and
again table cells are reused so for setting up data do it in another
function then reload TableView.
Solution for your problem should be look like that.
import UIKit
class StirrViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
//make tableView IBOutlet for reloading data
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var mylabel: UILabel!
var myString = String()
#IBAction func stirrBtn(_ sender: AnyObject) {
}
var timeSelected = String()
var selectedTimeArr = [String]()
override func viewDidLoad() {
super.viewDidLoad()
mylabel.text = myString
self.timeSelected = myString
self.setdefaultSwitchState()
}
//recipeSettings.boolStirrSwitch should be decleard like that
var switchState = [String : Bool]()
//setDeaultSwitchState
func setdefaultSwitchState(){
if let timeSelected = Int(self.timeSelected){
for value in 0..<timeSelected{
switchState["\(value)"] = false
//or
//recipeSettings.boolStirrSwitch["\(value)"] = false
}
}
self.tableView.reloadData()
}
#objc func switchChanged(_ sender : UISwitch!){
print("table row switch Changed \(sender.tag)")
print("The switch is \(sender.isOn ? "ON" : "OFF")")
let kValue = (sender.tag + 1)
let keyValue = String(kValue)
if sender.isOn {
switchState[keyValue] = true
} else {
switchState[keyValue] = false
}
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
let stringNum = Int(self.timeSelected)
recipeSettings.recipeTimeSet2 = stringNum!
return(stringNum)!
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UITableViewCell
//here is programatically switch make to the table view
let switchView = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
switchView.tag = indexPath.row // for detect which row switch Changed
switchView.addTarget(self, action: #selector(self.switchChanged(_:)), for: .valueChanged)
cell.accessoryView = switchView
cell.textLabel?.text = "\(indexPath.row + 1)"
if let switchState = switchState["\(indexPath.row)"] {
if switchState{
switchView.isOn = true
}else{
switchView.isOn = false
}
}else{
switchView.isOn = false
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

TableView not loading cells

My table view isn't loading the custom cells that are put into an array when I do a certain function. I know the the array contains the cells, but am at a loss as to why the cells aren't showing. It did work before but I made a few changes elsewhere in the code and now it does not work. Below is the code where I believe the error might possibly be:
#IBOutlet weak var incomeExpenseTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
incomeExpenseTableView.reloadData()
incomeExpenseTableView.delegate = self
incomeExpenseTableView.dataSource = self
}
override func viewDidAppear(animated: Bool) {
incomeExpenseTableView.reloadData()
}
#IBAction func addButtonPressed(sender: AnyObject) {
let addMessage = SCLAlertView()
addMessage.addButton("Income") {
self.performSegueWithIdentifier("incomeSegue", sender: self)
}
addMessage.addButton("Expense") {
self.performSegueWithIdentifier("expenseSegue", sender: self)
}
addMessage.showInfo("New Entry", subTitle: "Select either an income or expense to add")
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let post = DATA_KEY._loadedPosts[indexPath.row]
if let cell = tableView.dequeueReusableCellWithIdentifier("IncomeExpenseCell") as? IncomeExpenseCell {
cell.configureCell(post)
return cell
} else {
let cell = IncomeExpenseCell()
cell.configureCell(post)
return cell
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return DataService.instance._loadedPosts.count
}
func onPostsLoaded(notif: AnyObject) {
incomeExpenseTableView.reloadData()
}
FYI DATA_KEY = DataService.instance
Thanks
if you have a tableviewcell's identifier named "IncomeExpenseCell", the "if let" will always be successful, why using else, you can use as! instead of as?, you don't need 'else' here, and IncomeExpenseCell() maybe will not satisfy your 'cell init'
if let cell = tableView.dequeueReusableCellWithIdentifier("IncomeExpenseCell") as? IncomeExpenseCell {
cell.configureCell(post)
return cell
}
let cell = tableView.dequeueReusableCellWithIdentifier("IncomeExpenseCell") as! IncomeExpenseCell {
cell.configureCell(post)
return cell
}

Resources