After receiving a new object, I call this func to insert a cell:
private func addCellToTheTop(recipe: Recipe) {
guard let recipeTableView = self.recipeTableView else { return }
recipesForShow.insert(recipe, at: 0)
recipeTableView.beginUpdates()
recipeTableView.insertRows(at: [IndexPath.init(row: 0, section: 0)], with: .automatic)
recipeTableView.endUpdates()
}
But I get an error
Why the number of section does not match?
If it is important:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func numberOfSections(in tableView: UITableView) -> Int {
return recipesForShow.count
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return TableCellConfig.spaceBetweenCells
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = UIColor.clear
return headerView
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//...
}
You are getting this exception because you are appending a new element to the array that is being used to determine the numberOfSection whereas you are inserting a new row into the tableView. To fix this you need to insert section instead of inserting new row, here's how:
recipeTableView.insertSections([0], with: .automatic)
Related
I am using below code for one fixed cell at the top and then list of items in below in a single tableview, I am able to load the data from firestore database, but the first item in from the database is not shown, it is kind of hidden behind the fixed "detailscell", rest the fixed cell and all other items from the database shows up, how to fix that?
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// print(posts)
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 && details1.count > 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "detailsCell") as! DetailsCellInHomeScreen
cell.set(details: details1[indexPath.row])
return cell
} else if posts.count > (indexPath.row - 1) {
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell
cell.set(post: posts[indexPath.row - 1])
return cell
} else {
return UITableViewCell()
}
}
TableView has a function like below:
// MARK: - Headers and Footers
extension MyTableViewController {
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return MyCustomView()
}
}
You can use this function to return any view you want as a section header. Also note that sectionHeader views are sticky in default style tableView (not grouped style!). So you can use this as a advantage.
There are many tutorials like this one on the internet to follow and learn how to build a custom section section header for UITableView
You should add header on the table view:
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// print(posts)
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell
cell.set(post: posts[indexPath.row - 1])
return cell
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: tableView.frame.width, height: 50))
let label = UILabel()
label.frame = CGRect.init(x: 5, y: 5, width: headerView.frame.width-10, height: headerView.frame.height-10)
label.text = "A Label on detailsCell"
headerView.addSubview(label)
return headerView
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
if the tableView has several section you can realize the section with section: Int object
tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? uses for create the header view and tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat uses for set header height
I am working on an app in which I need to show multiple rows with a header. In my case only one section is showing. I have searched everything but can't find a suitable solution.
Here is my code:
class Timeline: UITableViewCell {
#IBOutlet weak var timelineData: UITextView!
}
class StudenTimelineViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let section = ["pizza", "deep dish pizza", "calzone"]
let items = [["Margarita", "BBQ Chicken", "Peproni"], ["sausage", "meat lovers", "veggie lovers"], ["sausage", "chicken pesto", "prawns & mashrooms"]]
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items[section].count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return section.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.section[section]
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TimelineId", for: indexPath) as! Timeline
let gpsData = items[indexPath.section][indexPath.row]
cell.timelineData.text = gpsData
return cell
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 40
}
What I am getting
How will I get all the sections. Thanks in advance.
This is because your method name func numberOfSectionsInTableView(tableView: UITableView) -> Int is incorrect and hence not called.
Replace the name with func numberOfSections(in tableView: UITableView) -> Int and see the magic happen.
//MARK: - Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "tableviewCell", for: indexPath)
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat
{
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
{
let cell: UITableViewCell
cell = tableView.dequeueReusableCell(withIdentifier: "tableviewHeader")!
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.backgroundColor = UIColor.white
return cell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 32;
}
Whenever I run the app, the tableView has no data, waiting for user to input. The problem is that if the numberOfSections is 1, it works just fine, but when I change it to 2 it crashes because Index out of range
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "expenseCell") as? ExpenseCell else { return UITableViewCell() }
let budget = userBudget[indexPath.section][indexPath.row]
cell.delegate = self
cell.configureCell(budget: budget)
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return UITableViewCellEditingStyle.none
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userBudget[section].count // <- Crash here
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Practice with Sections \(section)"
}
It is because you are accessing index 1 of userBudget while i assume it contains 0 index only.
This crash happens cause your array userBudget, don't has two elements at the moment you try access his second position.
You must guard that userBudget has two elements on minimium...
You should that you must to assign value to userBudget on your ViewDidLoad.
You are saying that you will have two section to your tableView's delegate, but you have an array which contains only one array. Basically when you try to reach userBadget[1] in your numberOfRowsInSection function and it crashes because it doesn't exist.
Replace
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
With
func numberOfSections(in tableView: UITableView) -> Int {
return userBudget.count
}
I created a tableView section for each day in my app. In simulator when I add data from beginning (tableview empty) it shows OK all the data, the section's name is the day, everything OK but when I change the date from my settings and add a new input I get an error that when I create the cell, it's out of bounds. I set some prints to see if its out of bounds and it seems to be in bounds but I don't know why I get this error
Code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "expenseCell") as? ExpenseCell else { return UITableViewCell() }
print("SECTION: \(daySections[indexPath.section].daySection)")
print("ROWS: \(daySections[indexPath.section].contentCount)")
let budget = userBudget[indexPath.section][indexPath.row] // <- error getting here
cell.delegate = self
cell.configureCell(budget: budget)
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return UITableViewCellEditingStyle.none
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Int(daySections[section].contentCount)
}
func numberOfSections(in tableView: UITableView) -> Int {
return daySections.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return daySections[section].daySection
}
Debug:
SECTION: Optional("21.10.2017")
ROWS: 2
SECTION: Optional("21.10.2017")
ROWS: 2
SECTION: Optional("22.10.2017")
ROWS: 1
fatal error: Index out of range
2017-10-16 15:41:30.284173+0300 Expense Manager[7877:380262] fatal error: Index out of range
(lldb)
I also set prints to show me indexPath.section and indexPath.row and when I get the error they are section: 1; row: 0.
I am calling batch updates(begin/end updates) on a tableView using this code:
let oldImageSet = Set(oldImageArray)
let newImageSet = Set(self.images)
let missingImages = newImageSet.subtracting(oldImageSet)
let missingImageIndexPaths = Array(missingImages).map{NSIndexPath(row: self.images.index(of: $0)!, section: 0)}
//Array(missingImages).map{NSIndexPath(forRow:newImageUUIDArray.indexOf($0)!,inSection:1)}
let removedImages = oldImageSet.subtracting(newImageSet)
let removedImageIndexPaths = Array(removedImages).map{NSIndexPath(row:oldImageArray.index(of: $0)!,section:0)}
self.tableView.beginUpdates()
if missingImageIndexPaths.isEmpty == false {
self.tableView.insertRows(at: missingImageIndexPaths as [IndexPath],with:.top)
}
if removedImageIndexPaths.isEmpty == false {
self.tableView.deleteRows(at: removedImageIndexPaths as [IndexPath],with:.none)
}
self.tableView.endUpdates()
Which is working alright.
Edit
This is my tableView section counts and how I have it set up:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 55
}
func numberOfSections(in tableView: UITableView) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
But now I have another tableView I would like to be able to call this on, But it has 'Sections' so When I use this I get a crash...?
Does anyone know how to use this code on a tableView with sections?
Many thanks in advance!
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 55
}
func numberOfSections(in tableView: UITableView) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
replace code with following code :
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 55
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
You should just apply what you're doing with rows to sections which means changing IndexPaths to IndexSets. IndexSet can be initialized with an array of Ints where each Int represents a corresponding section.
let oldImageSet = Set(oldImageArray)
let newImageSet = Set(self.images)
let addedImages = newImageSet.subtracting(oldImageSet)
// map each image to the section equivalent to its index in `self.images`
let addedImageSections = Array(missingImages).map{ self.images.indexOf($0)! }
let addedImageIndexSet = IndexSet(addedImageSections)
// repeat above process but in reverse to find images that have been removed
let removedImages = oldImageSet.subtracting(newImageSet)
let removedImageSections = Array(removedImages).map{ oldImageArray.index(of: $0)! }
let removedImageIndexSet = IndexSet(removedImageSections)
self.tableView.beginUpdates()
// if images have been added insert new sections
if !addedImageIndexSet.isEmpty {
self.tableView.insertSections(addedImageIndexSet, with: .top)
}
// if images have been deleted remove sections
if !removedImageIndexSet.isEmpty {
self.tableView.deleteSections(removedImageIndexSet, with: .none)
}
self.tableView.endUpdates()