I have been reading similar questions and until now neither has solved this error or I have not found it.
I have a TableView and I need the table to have a specific number of rows.
Now it works with a count of notifications from a array. In this array, I keep the notifications that come from the server.
But I want that if this array count is greater than 40, the rows are 40 and they not are the count of the array.
This is my code:
class ClassName: UITableViewController, UITabBarControllerDelegate {
public var notifications: [APINotification] = []
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.delegate = self
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 140
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(_ animated: Bool) {
self.notifications = []
self.getLastNotifications()
APIAuth.shared.count_badge = 0
self.tabBarController?.tabBar.items![0].badgeValue = nil
}
public func getLastNotifications() {
let req = Notification()
req.getLastNotifications(onComplete: {events in
self.notifications = events
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
})
}
override func numberOfSections(in tableView: UITableView) -> Int {
return self.notifications.count
}
// There is just one row in every section
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
// Set the spacing between sections
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let event = self.notifications[indexPath.section]
let cell = tableView.dequeueReusableCell(withIdentifier: "controller", for: indexPath) as! Controller
cell.bodyLbl.text = event.description
cell.typelbl.text = event.type
cell.typelbl.sizeToFit()
cell.titleLbl.text = event.title
cell.subjectLbl.text = event.subject?.name
return cell
} }
Thanks for your help!
You can change this method to :
override func numberOfSections(in tableView: UITableView) -> Int {
return self.notifications.count > 40 ? 40 : self.notifications.count
}
This will show as many rows as you got from your notification but if it's greater than 40 it will display only 40. To make it display 40 all the time just put return 40 instead but this may crash your app it the array will contain less items than 40.
Ps you display 40 sections no rows to change it to rows instead you need to chance your code to:
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.notifications.count > 40 ? 40 : self.notifications.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let event = self.notifications[indexPath.row]
...
}
There are several ways to achieve this:
Simple and consice:
return min(self.notifications.count, 40)
A Swifty approach:
guard self.notifications.count < 40 else {
return 40
}
return self.notifications.count
An alternative more verbose version:
return self.notifications.count < 40 ? self.notifications.count
A different more simple version:
if self.notifications.count > 40 {
return 40
} else {
return self.notifications.count
}
Use which ever version you feel best suits the purpose.
I would probably go for 1 or 2 if I felt like being all Swifty (is that even a word).
Try the following code:
// There is just one row in every section
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.notifications.count > 40{
return 40
}
return self.notifications.count
}
To limit the maximum number of sections, just check the array size:
override func numberOfSections(in tableView: UITableView) -> Int {
var count = self.notifications.count
if (count > 40) {
count = 40
}
return count
}
Related
My scenario is that I have three different types of arrays that might or might not contain values. I have 3 sections with section headers for my tableview. I am having trouble finding a solution that would be to dynamically set the sections i.e, if one of my arrays doesn't have a value then I don't want to show the section. If 3 arrays have value then show the 3 sections or if any one of the arrays doesn't have value then I don't want to show that section.
Your numberOfSections will be the number of arrays. And the numberOfRowsInSection will be the count of each arrays for that section in your tableViewDataSource.
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return array1.count
} else if section == 1 {
return array2.count
} else {
return array3.count
}
}
If there are no items in an array, then the rows will be zero for that section.
You can do something like this
// Create enum for simplifying the implementation.
enum SectionType{
case type1
case type2
case type3
}
class TestVC: UIViewController {
// create to show sections only when data is available
var sections: [SectionType] = []
// create you array types
var array1 = [String]()
var array2 = [String]()
var array3 = [String]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
// Add enums to section only when array has some value
// You can do this when you get API data
if array1.count > 0{
sections.append(.type1)
}
if array2.count > 0{
sections.append(.type2)
}
if array3.count > 0{
sections.append(.type3)
}
}
}
extension TestVC: UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
// only those sections with data wil be visible
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch sections[section]{
case .type1:
return array1.count
case .type2:
return array2.count
case .type3:
return array3.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch sections[indexPath.section]{
// return different type of cells if required accourding to the data in array
case .type1, .type2, .type3:
return UITableViewCell()
}
}
}
If the arrays can change dynamically (after the view is loaded), you can implement number of sections like that:
func numberOfSections(in tableView: UITableView) -> Int {
var numberOfSections = 0
if array1.count > 0 { numberOfSections++ }
if array2.count > 0 { numberOfSections++ }
if array3.count > 0 { numberOfSections++ }
return numberOfSections
}
Your data source should look like this - sectionModels = [[cellModels]]
The outer array represents number of sections and inner array represents number of cells in that section.
func numberOfSections(in tableView: UITableView) -> Int {
return sectionModels.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sectionModels[section].count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellModel = sectionModels[indexPath.section][indexPath.row]
// Configure UITableViewCell with cellModel
}
}
When I try to run my code, the cellForRowAtIndexPath function does not run for some reason.
The numberOfSections function works perfectly. I was thinking it was the dataSource/delegate function or a problem with the cell.
Would really appreciate finding solution to this problem.
import UIKit
class MoviesTableViewController: UITableViewController {
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
MovieController.getAllMovies(name: "blue") { (sucess) in
if sucess {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
tableView.reloadData()
}
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 MovieController.movies.count
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 148
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "movieCell", for: indexPath) as! MoviesTableViewCell
let movie = MovieController.movies[indexPath.row]
cell.movieRating.text = String(movie.vote_average)
cell.movieRating.text = movie.overview
cell.movieTitle.text = movie.title
return cell
}
Print Debugging
override func numberOfSections(in tableView: UITableView) -> Int {
print("Number of section called")
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Number of rows in section called")
print("Movies count: ", MovieController.movies.count)
return MovieController.movies.count
}
If Number of section called not printed to console: you have issue with tableView and It's DataSource
else if Movies Count: 0: tableView datasource working well and It's your MoviewController.movies issue. Check your MovieController
For the next step I need to see MoviewController content.
First you don't need to make tablevew.delegate = self in UItableViewController its already known , but this not the issue
You should make sure that your datasource MovieController.movies
have data before you call , just print movies before you call
self.tableview.reloadData()
There are number of cases which prevent your cellForRowAt to be called.
If your array MovieController.movies has 0 objects/elements, which you have return as MovieController.movies.count in your numberOfRowsInSection.
If you have placed your UITableView in storyboard or xib and because of any reason your UITableView don't get enough height to show your content in that case also your cellForRowAt will never be called. In most of the cases the height is 0.
I not find what is my problem.
I need to give space between one cell and another.
I wrote self.tableView Storyline.delegate = self because I read that this could be the problem, but not know if it is correct.
This is my code:
class ClassName: UIViewController, UITableViewDataSource, UITabBarControllerDelegate, UITableViewDelegate {
public var notifications: [APINotification] = []
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.delegate = self
tableViewStoryline.rowHeight = UITableViewAutomaticDimension
tableViewStoryline.estimatedRowHeight = 140
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(_ animated: Bool) {
self.notifications = []
self.getLastNotifications()
APIAuth.shared.count_badge = 0
self.tabBarController?.tabBar.items![0].badgeValue = nil
}
public func getLastNotifications() {
let req = Notification()
req.getLastNotifications(onComplete: {events in
self.notifications = events
DispatchQueue.main.async(execute: {
self.tableViewStoryline.delegate = self
self.tableViewStoryline.dataSource = self
self.tableViewStoryline.sectionHeaderHeight = 10
self.tableViewStoryline.reloadData()
})
})
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
// There is just one row in every section
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.notifications.count > 4 {
return 4
} else {
return self.notifications.count
}
}
// Set the spacing between sections
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell = tableView.dequeueReusableCell(withIdentifier: "controller")! as UITableViewCell
titleLbl.text = notifications[indexPath.row].title
bodyLbl.text = notifications[indexPath.row].description
typeLbl.text = notifications[indexPath.row].type
return cell!
}
}
Does anyone have any idea what the problem is?
Is there some code missing?
Thanks!
I think this is what you want:
func numberOfSections(in tableView: UITableView) -> Int {
if self.notifications.count > 4 {
return 4
} else {
return self.notifications.count
}
}
// There is just one row in every section
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
Then you have to change also cellForRowAt to take this into account.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell = tableView.dequeueReusableCell(withIdentifier: "controller")! as UITableViewCell
titleLbl.text = notifications[indexPath.section].title
bodyLbl.text = notifications[indexPath.section].description
typeLbl.text = notifications[indexPath.section].type
return cell!
}
Instead of having one section with notifications.count rows, you want notifications.count sections and each section with one row. Now setting 10 points as height for section header will make cells appear as having spaces between them.
There are some other options that you can consider, see my other answer.
you are using wrong delegate function.
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
is used for the hieght of header not for height of cell.
use func tableView(UITableView, heightForRowAt: IndexPath)
you will get correct sized cells.
Hope this helps!
I have a problem that I want to show a tableview, but separated in sections by the "status" of each item. I know how to do it with a simple string array, but I can't get to make this work with a class (Aluno) array, here's my code so far:
import UIKit
class DeliveriesTVC: UITableViewController {
let sections = ["Delivered", "Not Delivered"]
var studentList: [Array<Student>] = [[], []]
override func viewDidLoad() {
super.viewDidLoad()
for i in 0...5{
studentList[0].append(Student(alunoNome: "Aluno \(i)", alunoImg: "dani_test", alunoStatus: "Delivered"))
}
for i in 6...10{
studentList[1].append(Student(alunoNome: "Aluno \(i)", alunoImg: "dani_test", alunoStatus: "Not Delivered"))
}
self.title = "Deliveries"
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cellEntrega = tableView.dequeueReusableCell(withIdentifier: "EntregaCell", for: indexPath) as? EntregaCell {
let entregaCell = studentList[indexPath.section][indexPath.row]
// here i call a function in my TableViewCell class that update the cell itself
cellEntrega.updateAlunoUI(Aluno: entregaCell)
return cellEntrega
} else {
return UITableViewCell()
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listaAlunos[section].count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sections[section]
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
}
In the output, i just get the "first section" showing, and without a name, even with me setting the name of each section and the number of sections. I've looked everywhere but i couldn't find a solution.
Your numberOfSections and titleForHeader methods are wrong, it should be
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sections[section]
}
Additionally, you should return self.sections.count instead of return 2 in numberOfSections being hardcoded as in case you add another object to the array, you will have to change the 2 to whatever elements the array has now.
For your numberOfSectionInTableView function, shouldn't it be override func numberOfSectionsInTableView(in tableView: UITableView) -> Int {
return 2
} with a in in front of the tableView: UITableView?
I don't see where you connect your UITableView's delegate and datasource.
Here is a tutorial to show your about using UITableView.
Take a look at "Basic Table View" - "Step 3: Set the datasource and delegate"
I'm trying to make a very simple indexed tableView in Swift 3.
I managed to add the index to the view, but as soon as I added the sections headers, every cell showed a strange separator line on the right side (see screenshot)
I have no idea where it could come from and it doesn't appear in other indexed tableview tutorials I saw online.
This is the code for the TableViewController:
class TableViewController: UITableViewController
{
var tableData : [String] = []
var indexOfNumbers : [String] = []
override func viewDidLoad()
{
super.viewDidLoad()
let numbers = "100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500"
tableData = numbers.components(separatedBy: " ")
let indexes = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"
indexOfNumbers = indexes.components(separatedBy: " ")
}
override func numberOfSections(in tableView: UITableView) -> Int { return indexOfNumbers.count }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = tableData[indexPath.section]
print(indexPath.section, indexPath.row, separator : " ")
return cell
}
override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int
{
let temp = indexOfNumbers as NSArray
return temp.index(of: title)
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
{
return "Section \(section)"
}
override func sectionIndexTitles(for tableView: UITableView) -> [String]?
{
return indexOfNumbers
}
}
What could I do?
Thanks
I believe you need to comment out or remove the following delegate function.
override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int
{
let temp = indexOfNumbers as NSArray
return temp.index(of: title)
}