Implementing static table view cells with data in an array - ios

I have some data which is pulled from a DB and stored in an array within my UITableViewController. However, when I try to put the data inside each of my cells, I get an error that my index is out of the bounds of the array.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellItems.count
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item: ItemPreBasketModel = cellItems[indexPath.row] as! ItemPreBasketModel
if indexPath.row == 0 {
//Desc cell
let cellIdentifier: String = "descCell"
let descCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
return descCell
} else if indexPath.row == 1 {
//Price cell
let cellIdentifier: String = "priceCell"
let priceCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
return priceCell
} else if indexPath.row == 2 {
//Quantity cell
let cellIdentifier: String = "quantityCell"
let quantityCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
return quantityCell
} else {
//Submit cell
let cellIdentifier: String = "submitCell"
let submitCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!
return submitCell
}
}
Here is the cellItems variable and also where it is populated:
var cellItems: NSArray = NSArray() {
didSet {
tableView.reloadData()
}
}
func itemsDownloaded(items: NSArray) {
cellItems = items
tableView.reloadData()
}
Here is the static table view in the storyboard:
What I want to do, which I've took out of the code for as it still fails before reaching this part, is to use the 'item' variable I declare inside cellForRowAt and populate each of my cell outlets with a part of the object,
i.e: descCell.descLabel.text = item.name
The error I get is:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
on the line
let descCell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier)!

You should not use a table view controller with static cells and cellForRowAt at the same time. You should use dynamic prototypes to populate an array dynamically.
To change the content type of your table view, select it in Interface Bulder, open the Attributes inspector (the fourth icon from the left in the right sidebar) and select Dyanmic Prototypes for Content under the Table View section.

Related

Swift Add and Remove Item into array based on UITableview cell select and deselect

In my case, I am loading JSON data into Tableview with help of codable. The tableview multiple select and deselect checkmark options available. Now, I need to Insert selected values into one array and same time if user deselect the cell need to remove relevant value from same array. The stored array data, I am going to use in multiple viewcontroller. How to achieve this?
JSON Codable
// MARK: - Welcome
struct Root: Codable {
let status: Bool
let data: [Datum]
}
// MARK: - Datum
struct Datum: Codable {
let userid, firstname, designation: String?
let profileimage: String?
}
My Codebase for Tableview Delegate
var studentsData = [Datum]()
var sessionData = [Datum]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.studentsData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
let item = self.studentsData[indexPath.row]
cell.nameCellLabel.text = item.firstname
cell.subtitleCellLabel.text = item.designation
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let item = self.studentsData[indexPath.row]
if let cell = tableView.cellForRow(at: indexPath) {
if cell.accessoryType == .checkmark {
cell.accessoryType = .none
// UnCheckmark cell JSON data Remove from array
if let index = sessionData.index(of:item) {
sessionData.remove(at: index)
}
print(sessionData)
} else {
cell.accessoryType = .checkmark
// Checkmark selected data Insert into array
self.sessionData.append(item)
print(sessionData)
}
}
}
You can remove the item inside the array by searching for its index
let index = try? array.firstIndex(where: { $0. userid == sessionData[indexPath.row].userid })
Or you can use this Checking if an array of custom objects contain a specific custom object

How to show alamofire post request json value in table view using Swift

am try to convert json data to table view for swift 5 and also alamofire 5.2 version I got my response data from server itz also covert json but the problem is I can't show my response data in table view
class ViewController: UIViewController, UITableViewDataSource {
struct User {
var buzzyuser_id:Int?
var buzzyuser_image:String?
var buzzyuser_username:String?
init(dic:[String: Any]) {
self.buzzyuser_id = dic["buzzyuser_id"] as? Int
self.buzzyuser_image = dic["buzzyuser_image"] as? String
self.buzzyuser_username = dic["buzzyuser_username"] as? String
}
}
#IBOutlet weak var TableView: UITableView!
private var users = [User] ()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
TableView.dataSource = self
apiload()
}
func apiload() {
let parameters: [String: Any] = ["userid": "1","start":"0"]
let url = "https://example.com/sample.php"
AF.request(url, method: .post, parameters: parameters).responseJSON { response in
switch response.result{
case .success:
//success, do anything
if let json = response.value as? [String: Any] {
for item in json {
// construct your model objects here
self.users.append(User(dic:json))
}
DispatchQueue.main.async {
self.TableView.reloadData()
}
}
break
case .failure:
return
}
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customcell")
let text = users[indexPath.row]
return cell!
}
}
debug value showed but list viewed only empty rows I can't find the error.
your problem is here
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->
UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customcell")
let text = users[indexPath.row]
return cell!
}
you are assigning the user value to the param "text" and then you are not assigning any value to the table view cell.
If you are trying... for example to show on the table the user "buzzyuser_username" then after
let text = user[indexPath.row]
you should add
cell?.textLabel?.text = text.buzzyuser_username
Also make sure you are registering the identifier "customcell" ... if you wish to load the cell from a xib file you can do it like this (note that with a xib file you can also create a custom subclass of UITableViewCell and make a tableview row with the user image id and username (that is what I think you are trying to archive)
self.table?.register(UINib.init(nibName: "YOUR_NIB_NAME", bundle: nil), forCellReuseIdentifier: "customcell")
And after registering the reuse identifier you can then use it on your table view and if you also have added a custom class to your identifier you can force it on your tableview datasource implementation
let cell = tableView.dequeueReusableCell(withIdentifier: "customcell") as! YOUR_CUSTOM_SUBCLASS

cell expand in tableview is not showing properly in swift

I am new to swift I have a tableView with a custom cell. When I hit the cell, it should expand the cell and show all the data. I tried but I am not able to get the proper result. I have to show a custom table view cell when I will show all the data
data. I have the data coming from json.
Custom xib table view cell:
class ExpandTableViewCell: UITableViewCell {
#IBOutlet weak var timeLable: UILabel!
var item: ExpandTableViewCell? {
didSet {
}
}
static var nib:UINib {
return UINib(nibName: identifier, bundle: nil)
}
static var identifier: String {
return String(describing: self)
}
}
Register into the viewDidLoad():
tableView?.register(ExpandTableViewCell.nib, forCellReuseIdentifier: ExpandTableViewCell.identifier)
The tableView methods:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 1 {
if storeDetailsDictionary != nil{
return (storeDetailsDictionary?.count)!
} else {
return 0
}
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(indexPath.section == 1) {
//address cell
let cell = tableView.dequeueReusableCell(withIdentifier: ExpandTableViewCell.identifier, for: indexPath) as? ExpandTableViewCell
var timing:String = String.init(htmlEncodedString: storeDetailsDictionary!["timing"] as? String ?? "")
cell?.timeLable.text = timing//?.replacingOccurrences(of:", ", with: ",\n")
return cell!
}
}
How can I expand the table view cell?
check this tutorial it's helpful for creating expandable list:
https://medium.com/ios-os-x-development/ios-how-to-build-a-table-view-with-collapsible-sections-96badf3387d0

iOS Swift: Getting repeated value while updating 2D Array in custom UITableView cell

I have a 2D Array which I want to populate in UITableView Custom Cell in a specific pattern.
//Retrieved from Parse backend
var myArray = [["Name1", "Age1"],["Name2", "Age2"],["Name3", "Age3"]]
//What I need is:
nameArray = ["Name1", "Name2", "Name3"]
ageArray = ["Age1", "Age2", "Age3]
So that I can use indexPath to fill the Name data in the custom UITableView cell For Ex: nameArray[indexPath.row]
I tried using the for in loop,
var nameArray = NSMutableArray()
var ageArray = NSMutableArray()
//Inside CellForRowAtIndexPath
for data in myArray {
self.nameArray.addObject(data[0])
self.ageArray.addObject(data[1])
}
cell.nameLabel.text = "\(nameArray[indexPath.row])"
cell.ageLabel.text = "\(ageArray[indexPath.row])"
But I am getting repetitive name and age label filled with Name1 and Age1 in both the cell. Does anyone know whats wrong in this?
Is there a better way to reload this data as needed?
// UPDATED FULL WORKING CODE Thanks to #l00phole who helped me solve the problem
class NewViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: UITableView!
var data = [[String]]()
var cost = Double()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
uploadData()
}
func uploadData() {
let query = PFQuery(className:"Booking")
query.getObjectInBackgroundWithId("X0aRnKMAM2") {
(orders: PFObject?, error: NSError?) -> Void in
if error == nil && orders != nil {
self.data = (orders?.objectForKey("orderDetails"))! as! [[String]]
//[["Vicky","21"],["Luke", "18"],["7253.58"]]
//*****Removing the last element as it is not needed in the tableView data
let count = self.data.count - 1
let c = self.data.removeAtIndex(count)
cost = Double(c[0])!
//******
} else {
print(error)
}
self.reloadTableData()
}
}
func reloadTableData()
{
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
return
})
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return data.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:NewTableViewCell = self.tableView!.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! NewTableViewCell
// Configure the cell...
cell.nameLabel.text = "\(data[indexPath.row][0])"
cell.ageLabel.text = "\(data[indexPath.row][1])"
return cell
}
You are adding to the nameArray and ageArray every time cellForRowAtIndexPath is called and you are not clearing them first. This seems inefficient and you should only populate those arrays when the input data changes, not when generating the cells.
I don't even think you need those arrays, as you could just do:
cell.nameLabel.text = "\(data[indexPath.row][0])"
cell.ageLabel.text = "\(data[indexPath.row][1])"
You don't have to create separate array for name and age, you can use the existing myArray as below
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:NewTableViewCell = self.tableView!.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! NewTableViewCell
// Configure the cell...
let dataArray = myArray[indexPath.row]
cell.nameLabel.text = "\(dataArray[0])"
cell.ageLabel.text = "\(dataArray[1])"
return cell
}
}

UITableView going out of view

i have a UITableView with multiple selection enabled with checkmarks. When i make selection that are all visible in the view, i don't run into any errors. However, if i scroll down further and place a selected item out of view, i get errors and even though the row stays selected, the checkmark goes away.
import Foundation
import Parse
import UIKit
class customerMenuVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var menuTV: UITableView!
var menuItems: [String] = ["Hello"]
var menuPrices: [Double] = [0.0]
var orderSelection: [String] = []
var priceSelection: [Double] = []
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return menuItems.count
}
func tableView(tableView: UITableView, numberOfColumnsInSection section: Int) -> Int
{
return 1;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell:UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "mycell")
cell.textLabel!.text = "\(menuItems[indexPath.row])\t $\(menuPrices[indexPath.row])"
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
//tableView.deselectRowAtIndexPath(indexPath, animated: true)
let cell = tableView.cellForRowAtIndexPath(indexPath)
cell!.accessoryType = .Checkmark
orderSelection.append(cell!.textLabel!.text!)
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath)
{
let cell = tableView.cellForRowAtIndexPath(indexPath)
cell!.accessoryType = .None
}
override func viewDidLoad() {
super.viewDidLoad()
menuTV.allowsMultipleSelection = true
let resMenu = resUser.sharedInstance
var resName = resMenu.nameStr
var resID = resMenu.idStr
var menuQ = PFQuery(className: "menu")
menuQ.getObjectInBackgroundWithId(resID){
(menus: PFObject?, error: NSError?) -> Void in
if error == nil && menus != nil {
let items: [String] = menus?.objectForKey("menuItems") as! Array
let prices: [Double] = menus?.objectForKey("menuPrices") as! Array
self.menuItems = items
self.menuPrices = prices
self.menuTV.reloadData()
}
}
}
#IBAction func continueButton(sender: AnyObject) {
let selections = menuTV.indexPathsForSelectedRows() as! [NSIndexPath]
var indexCount = selections.count
println(indexCount)
var x = 0
while x < indexCount
{
println(x)
let currentCell = menuTV.cellForRowAtIndexPath(selections[x]) as? UITableViewCell?;
println(x)
println(selections[x].row.description)
orderSelection.append(currentCell!!.textLabel!.text!)
println(orderSelection[x])
x++
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
This is how table views work.
When a cells scrolls off-screen, it gets tossed into the recycle queue and then used again to display data for a different indexPath in your data.
Any time the user makes any changes to the data for a cell you should save it to your data model (usually an array of information, or maybe an array of arrays if you're using a sectioned table view.) Then you should tell the table view to redisplay the changed cell. The cellForRowAtIndexPath method picks up the changed data and shows the changes to the cell. If the cell scrolls off-screen and then scrolls back on-screen, it gets displayed with the correct settings.
This applies to keeping track of which cells are selected as well.

Resources