I have a table view that keeps track of hotel receipts (each time the user enters the date, cost, and name of the hotel and hits the add button it adds a new row to the table with the information.
I have 2 text fields below the table that I want to show the total entries (number of rows) and the sum of the cost field. Problem is I can't figure out how to do this or if it is even possible.
I have found a couple posts about it but they all seem to have a set number of rows.
extension HotelViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
let numberOfSections = frc.sections?.count
return numberOfSections!
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let numberOfRows = frc.sections?[section].numberOfObjects
return numberOfRows!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HotelCell", for: indexPath) as! HotelTableViewCell
let item = frc.object(at: indexPath) as! DriveAwayHotel
cell.date.text = item.date
cell.name.text = item.name
cell.cost.text = "$\(item.cost ?? 0.00)"
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
let managedObject : NSManagedObject = frc.object(at: indexPath) as! NSManagedObject
pc.delete(managedObject)
do {
try pc.save()
} catch {
print(error)
return
}
}
You should not try to do math on your cells. You should do math on your model.
Are you trying to sum all the entries in your table, or just the cells that are visible?
If you want to sum all the entries in your table view, then loop through all the sections in your frc.sections array, loop through all the entries in each section, and add them all up. (That code would be easy to write.)
If you only want to sum the entries for the currently visible cells, call the table view's indexPathsForVisibleRows method to get an array of the indexPaths of the cells that are visible, loop through those indexPaths, fetch the entry at each section and row, and add those together. (That code would also be easy to write.)
EDIT:
The code to sum all your entries might look something like the below (I'm having to guess a bit since I don't know your data model)
var total = 0.0
guard let sections = frc.sections?.count else { return }
for section in 0..<sections {
guard let rows = frc.sections?[section].numberOfObjects else { continue }
for row in 0..<rows {
let indexPath = IndexPath(row: row, section: section)
let item = frc.object(at: indexPath) as! DriveAwayHotel
total += item.cost
}
}
Related
How to implement two xib cells and only one array, if array count is nil show empty xib cell and if array count is not nil show xib cell with data swift. Please solve the problem i have searched lot of no answer is related to me.
This example assumes your data array is named myData:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return either 1, or the count of your data, whichever is greater
return max(1, myData.count)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// if it's the first row, and your data is empty
if indexPath.row == 0 && myData.count == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "emptyCell", for: indexPath) as! EmptyCell
return cell;
}
let cell = tableView.dequeueReusableCell(withIdentifier: "dataCell", for: indexPath) as! DataCell
// populate the cell
return cell
}
I'm building an app which presents departures of busses. I use a tableview to represent it, the name of the busstop is set as the section header, and each row in a section represents a departure from that busstop.
The API always provide me with the next 20 departures for each stop, but initially I just show the next 6 departures in each section, I keep all the 20 in my datasource though. At the end of each section I have a cells which is supposed to double the shown departures in each section. It's done it this way:
tableView.beginUpdates()
tableView.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: .none)
tableView.endUpdates()
My initial thought was that I could just double the returnvalue of the numberOfRowsInSection-function for the specific section, but that doesn't seem to work.
var scale = [Int : Int]()
func numberOfSections(in tableView: UITableView) -> Int {
if stops.count != nil {
return stops.count
} else {
return 1
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return stops[indexPath.row].departures.count - scale[stops[section].id]
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if stops != nil {
let currentDeparture = stops[(indexPath as NSIndexPath).section].departures![(indexPath as NSIndexPath).row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! DepartureTableViewCell
// Configuration of the cell
return cell
}
}
return UITableViewCell()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == (stops[indexPath.row].departures.count)! {
var currentScale = scale[stops[indexPath.section].id]
scale[stops[indexPath.section].id] = currentScale - 6
tableView.beginUpdates()
tableView.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: .none)
tableView.endUpdates()
}
}
The dictionary scale is just mapping the ID of the stop to the number of departures that should be shown, starting at 14 (20-6) and each time the cell that is supposed to reload the section is tapped, it get reduced by 6. So we get 6 more departures in the given section. currentDepartureresInSection is the number of departures at the specific stop.
It is possible to do it this way, or do I have to update the datasource of the tableview?
I call tableView.reloadData() inside of didSet{} of the items variable.
I got a crash in the cellForRowAt function only once, never before, never after with no code changes.
override func tableView(_ tableView: UITableView, number ofRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.tag = indexPath.row
let item = items[indexPath.row] //Thread 1: Fatal error: Index out of range
cell.textLabel?.text = item.title
The possible solution will be you need to handle to solve the crash.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(indexPath.row > items.count-1){
return UITableViewCell()
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.tag = indexPath.row
let item = items[indexPath.row] //Thread 1: Fatal error: Index out of range
cell.textLabel?.text = item.title
return cell
}
}
Maybe this will help.
I can see it happening if the table is scrolling quickly and you update the data source: it could hit a race condition where it tries to call cellForRowAt one more time before it realizes you changed the data source. As a precaution, I'd suggest always adding a check that your index is less than the array's count. Maybe it's paranoid, but better than a crash.
e.g:
Array has 100 items.
Swipe hard sending you towards bottom of table.
While scrolling, the array is updated to only have 10 items.
Table scroll asks for cell at row 99 because it didn't get the message yet.
Crash when you ask for item 99 in an array with only 10 items.
This crash appears when your index is more than array's count.
This crash may appear when scrolling the table view and table view get's updated. You are trying to fetch the index more than array count.
You can check the condition of indexpath.row less than array count.
Try using some thing like this
var fileNameArray = [Audio](){
didSet{
if fileNameArray.count > 0{
recordingListTableView.reloadData()
}
}
}
Is It required to do this ?
crash Is index out of range
Are you Re-Updating Array After this didSet Statement ? if Yes, Do not use this DidSet case here , use if Array is not going to be updated again ,
If you still want to use , Also make use of WillSet Case After didSet()
override func tableView(_ tableView: UITableView, number ofRowsInSection section: Int) -> Int {
guard let items.count > 0 else {return 0}
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.tag = indexPath.row
let item = items[indexPath.row]
cell.textLabel?.text = item.title
}
I think items array does not have any value so it does crash. So, try this above code.
I Have a UITableView which is controlled by NSFetchedResultsController. I want to add single cell to the first row and make this cell static. In other words, there will be a button which will open another View Controller.
Until now, I was ok with fetched results controller and table. Now I'm a bit confused. How should I do this?
Instead using a header might be ok too, but I don't want this header to be on top all the time. I want this cell to be just like WhatsApp iOS "Create new group" cell on chats panel.
Thank you!
var dataArray = ["A","B","C"]
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.dataArray.count+1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if indexPath.row == 0
{
let cell = tableView.dequeueReusableCell(withIdentifier: "CreateNewGroupCell") as! CreateNewGroupCell
return cell
}
else
{
// Get the data from Array
let data = self.dataArray[indexPath.row-1]
// Logic to show other cells
let cell = tableView.dequeueReusableCell(withIdentifier: "OtherCell") as! OtherCell
return cell
// ....
}
}
You will need to create tableview with number of rows fetched from NSFetchedResultsController +1. Also in cellForRowIndex method you will need to add a check like indexPath.row == 0 and in there you will make the changes.
Also you will have to add action for that button within that section. You can also set different custom tableview for first row.
It can be similar to following:
func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
if(indexPath.row==0){
let cell = tableView.dequeueReusableCell(withIdentifier: "CellWithButton", for: indexPath) as! CellWithButton
}
else{
let cell = tableView.dequeueReusableCell(withIdentifier: "OtherCells", for: indexPath) as! OtherCells
//here add data for cells from your array
}
return cell
}
My Situation: I want to save Data from an Array at Index X in an Row on Index X in Section 1.
My code is:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return setObjectToPass.count
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Section \(section)"
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cellEmpty = tableView.dequeueReusableCellWithIdentifier("LabelCell")
var countCell = 0
while countCell < setObjectToPass.count {
let indexPaths = NSIndexPath(forRow: countCell, inSection: 0)
let cell = tableView.dequeueReusableCellWithIdentifier( "LabelCell", forIndexPath: indexPaths)
cell.textLabel!.text = String(setObjectToPass[countCell])
print(cell)
countCell+=1
return cell
}
My Problem is that only the first index of the Array SetObjectToPass is passed and set into the Cell.text
while counter < fetchResult?.count {
let set = fetchResult![counter]
counter+=1;
setObject.append((set.reps?.integerValue)!)
}
You are implementing the tableView(_:cellForRowAtIndexPath:indexPath:) method wrongly.
Remember, every delegate method in UITableViewDelegate is like asking you a question. For example, numberOfSectionsInTableView(_:) is like asking you "How many sections do you want in your table view?". You answer the question by returning a value.
tableView(_:cellForRowAtIndexPath:indexPath:) is similar. It asks a question as well. It asks "What should I display in the table row at this index path?"
In your code, it seems like you want to give multiple answers - looping through the array and attempting to return multiple times. But it doesn't work that way, you can only give one answer.
In the first iteration of the while loop, the execution hits return and stopped. That's why you only see the first table cell.
Thus, you should change your code so that it only gives one answer to the question:
let cell = tableView.dequeueReusableCellWithIdentifier("LabalCell")
cell.textLabel?.text = String(setObjectsToPass[indexPath.row])
return cell
Don't use the loop in cellForRowAtIndexPath delegate method. cellForRowAtIndexPath method call each row based upon numberOfRowsInSection count rows, simply use indexpath.row, Use this code,
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cellEmpty = tableView.dequeueReusableCellWithIdentifier("LabelCell")
let cell = tableView.dequeueReusableCellWithIdentifier( "LabelCell", forIndexPath: indexPaths)
cell.textLabel!.text = setObjectToPass[indexPath.row] as? String
return cell
}
hope its helpful