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.
Related
i have a tableview in a viewcontroller and because i need to reuse most of the code for another table i created an extra class:
class StatisticsViewDelegate: NSObject, UITableViewDelegate, UITableViewDataSource {
var defaultList:[String]
var infolist:[String] = []
var tableView:UITableView
var controller:UIViewController?
init(defaultList:[String], view:UITableView, controller:UIViewController?) {
self.defaultList = defaultList
self.controller = controller
tableView = view
super.init()
tableView.delegate = self
tableView.dataSource = self
loadTable()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return infolist.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "infocell", for: indexPath) as! TableViewCell
// [fill cell]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// [...]
}
func loadTable() {
DispatchQueue.global(qos: .userInitiated).async {
//[...]
// in this case:
self.infolist = self.defaultList
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
and in my UITViewController in the viewDidLoad():
delegate = StatisticsViewDelegate(defaultList: defaultList, view: tableView, controller:self)
delegate is a member of the ViewController
now when i run it, the function func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) never gets called. The func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) gets called however(before and after the reload) and returns the correct number(in my case 4). The TableView isn't visible at all. Where is my error?
Maybe you can use the subclassing strategy to resolve your problem. There are many reference passed to your class and if you forgot to clean that up you will be have memory leaks in your hand. So I'll suggest the simple example as below. You can modify as you like and let me know if that was what you are after. If not please pardon me.
//This will be parent class that will handle all table methods, so you need to write only once the delegates and stuffs
class MyCommonTableController: UITableViewController {
var infoList = [String]()
// MARK: - TableView Delegate and Datsource Impl
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return infoList.count
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 55.0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = infoList[indexPath.row]
return cell
}
}
The first class that is directly subclassing the from above MyCommonTableController
//In here we just have to know the data and set the infoList from parent
class TheTableViewController: MyCommonTableController {
let defaultList = ["Data1","Data2","Data3"] //....etc
override func viewDidLoad() {
super.viewDidLoad()
//this is were I will set those
infoList = defaultList
//reload the table
tableView.reloadData()
}
}
The second class that is directly subclassing the from above MyCommonTableController. Same process goes here
class TheSecondTableViewController: MyCommonTableController {
let defaultList = ["List1","List2","List3"] //....etc
override func viewDidLoad() {
super.viewDidLoad()
//this is were I will set those
infoList = defaultList
//reload the table
tableView.reloadData()
}
}
And now you are not repeating and table methods. You can also get the reference of table and use in your norma table view
#IBOutlet weak var theTable: UITableView!
let defaultList = ["List1","List2","List3"] //....etc
let commonTable = MyCommonTableController()
// MARK: - LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
commonTable.infoList = defaultList
commonTable.tableView = theTable
}
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!
The table cells contained in the first view are properly represented, but the table cells contained in the second view do not appear. I can’t understand why.
HelpController.swift
import UIKit
class HelpController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// MARK: - Initialize
#IBOutlet weak var menuTable: UITableView!
let helpMenu = ["a","b","c"]
override func viewDidLoad() {
super.viewDidLoad()
//datasource link
menuTable.delegate = self
menuTable.dataSource = self
self.menuTable?.tableFooterView = UIView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table View Data Source
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return helpMenu.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "HelpMenuCell")
cell.textLabel?.text = helpMenu[indexPath.row]
return cell
}
}
When i check it with print (helpmenu.count), it return 3. It seems to work well until numberOfRowsInSection, but cellForRowAt does not work.
and this is my first view InfosController.swift
// MARK: - Table View Data Source
// get cell count
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if UserDefaults.standard.value(forKey: "userid") != nil {
// sign in state
return memberMenu.count
} else {
// sign out state
return nonMemberMenu.count
}
}
// change cell text
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "InfosMenuCell")
if UserDefaults.standard.value(forKey: "userid") != nil {
// sign in state
cell.textLabel?.text = memberMenu[indexPath.row]
} else {
// sign out state
cell.textLabel?.text = nonMemberMenu[indexPath.row]
}
return cell
}
This is the code included in the first view that works properly. This works well, but I’m confused because it does not work well in the second view(HelpController).
ps.
The problem in HelpController, In controlle self.menuTable?.tableFooterView = UIView()
So if You are having footer view then You need to write the delegate function heightForFooterInSection and viewForFooterInSection
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
}
Make sure below things
Make sure you added Delegate & DataSource to tableView.
Check your helpMenu count is not equal to
Zero.
If numberOfRowsInSection returns Zero, then probably cellForRowAt
indexPath won't call.
Please check the method declaration once the correct syntax is
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell { }
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"
import UIKit
class exploreViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var exploreTableView: UITableView!
var CELLHEIGHT = 200
var SECTIONHEADER = "SECTIONHEADER"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
exploreTableView.delegate = self
exploreTableView.dataSource = self
exploreTableView.register(UINib(nibName: "answerCell", bundle: nil), forCellReuseIdentifier: "cell1")
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return SECTIONHEADER
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.exploreTableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! answerCell
cell.name.text = "222222"
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return CGFloat(CELLHEIGHT)
}
}
My numberOfSectionsInTableView is never called and I can't get 2 sections.
From your code, I believe you are using Swift3. Then, the following are delegate and datasource methods for UITableView in Swift3
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
// Configure the cell...
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
As you can see, your numberOfSections function's syntax is wrong.That is the reason.
This is actually due to access level resolution. When we don't specify the "public" ourselves, the compiler resolves it as to be some privately implemented function and even warns that it nearly matches a public function. If one ignores this then it neglects this function and rather calls the default implementation. Hope this helps someone.
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}