I have a dictionary which will be a data source for my tableView.
However, I want to add also other cells that are not the part of the dictionary, which means that they should not be under the section title that comes from dictionary.
To give you a better notion of what I'm trying to achieve I created this exemplary project available here
Here is the ViewController implementation, I tried to keep it as short as possible:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let dict2:Dictionary<String,[String]> = ["SECTION1":["This should be under section", "This should be under section too"]]
var dictKeysSorted:[String] = []
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
dictKeysSorted = dict2.keys.sort()
self.tableView.delegate = self
self.tableView.dataSource = self
// Do any additional setup after loading the view, typically from a nib.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (dict2 as NSDictionary).objectForKey(dictKeysSorted[section])!.count + 1
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return dictKeysSorted[section]
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30.0
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return dictKeysSorted.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: CustomCell = tableView.dequeueReusableCellWithIdentifier("cell") as! CustomCell
if indexPath.row == 0
{
cell.label.text = "This cell should be above section"
}
else
{
cell.label.text = (dict2 as NSDictionary).objectForKey(dictKeysSorted[indexPath.section])![indexPath.row-1] as? String
}
return cell
}
}
This is what I get:
My first idea was to create another record in my dictionary and simply omit it using if-statement in
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String?
But this is not the proper solution since it keeps returning an extra empty section header.
How to make tableView "omits" certain section headers?
Thanks in advance
PS. I found it difficult to name this thread properly, if you have any idea how to name it in a more relevant way, feel free to edit the title of this question.
You can implement the UITableViewDelegate method that returns the height of the header returning 0 for the header that you want to hide:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
Here is the reference.
As stated in the doc, since iOS5 returning an empty header view or an empty title no longer result in an invisible header.
Please note that this will require you to return the default height for the sections that you want to show.
Related
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 am trying to create a tableView for each value in my array and I am not sure how to go about creating that. The code below clearly doesn't work I just used it to illustrate my point. How do I differentiate between tables in the tableView protocol functions.
let array = ["1","2","3","4","5"]
for value in array{
let newTableView: UITableView()
//Continue with creation of table
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableContent.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return cell
}
Any help is appreciated and please comment if the question is unclear
Previously, I have needed multiple TableViews or Collection Views in a single ViewController, e.g. for different tabs.
You can identify each table by using the tag property.
let data : [Int : [YourObjects]]
for key, value in data {
let newTableView: UITableView()
// Identify the table
newTableView.tag = key
newTableView.dataSource = self
//Continue with creation of table
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.data[tableView.tag].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Dequeue cell
// Get the data for this table, identified by the tag
let tableData = data[tableView.tag]
cell.populateCell(withData: tableData[indexPath.row])
return cell
}
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"
Trying to conform to UITableViewDataSource and UITableViewDelegate inside a Swift UIViewController subclass.
class GameList: UIViewController {
var aTableView:UITableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
aTableView.delegate = self
aTableView.dataSource = self
self.view.addSubview(aTableView)
//errors on both lines for not conforming
}
}
Docs say you should conform on the class line after the : but that's usually where the superclass goes. Another : doesn't work. Using a comma separated list after the superclass also doesn't work
EDIT:
Also must adopt all required methods of each protocol, which I wasn't initially doing.
You use a comma:
class GameList: UIViewController, UITableViewDelegate, UITableViewDataSource {
// ...
}
But realize that the super class must be the first item in the comma separated list.
If you do not adopt all of the required methods of the protocol there will be a compiler error. You must get all of the required methods!
As XCode6-Beta7 releases,
I noticed the protocol method of UITableViewDataSource changed a little bit and sounded the same conform to protocol error which worked fine in beta6.
These are the required methods to be implemented according to the UITableViewDataSource protocol:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // insert code}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // insert code
}
You might want to re-check the difference or re-implement the delegate method that you thought you just implement.
You must implement two require methods here:
func tableView(tableView:UITableView!, numberOfRowsInSection section:Int) -> Int {
return 10
}
func tableView(tableView:UITableView!, cellForRowAtIndexPath indexPath:NSIndexPath!) -> UITableViewCell! {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "MyTestCell")
cell.text = "Row #\(indexPath.row)"
cell.detailTextLabel.text = "Subtitle #\(indexPath.row)"
return cell
}
Also, it is important to copy all the non optional functions from the Delegate class. Cmd + Click on the UITableViewDatasource
and copy those two definitions as is.
For me in beta7, the UITableViewDatasource has
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
My implementation:
var items = ["Apple", "Pear", "Banana"]
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "Default")
cell.textLabel?.text = items[indexPath.row]
cell.detailTextLabel?.text = "Test"
return cell
}
Usee These methods:
There is change in Data source methods-
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
protocol UITableViewDataSource : NSObjectProtocol {
****func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell****
optional func numberOfSectionsInTableView(tableView: UITableView) -> Int // Default is 1 if not implemented
optional func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? // fixed font style. use custom view (UILabel) if you want something different
optional func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String?
// Editing
// Individual rows can opt out of having the -editing property set for them. If not implemented, all rows are assumed to be editable.
optional func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool
// Moving/reordering
// Allows the reorder accessory view to optionally be shown for a particular row. By default, the reorder control will be shown only if the datasource implements -tableView:moveRowAtIndexPath:toIndexPath:
optional func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool
// Index
optional func sectionIndexTitlesForTableView(tableView: UITableView) -> [AnyObject]! // return list of section titles to display in section index view (e.g. "ABCD...Z#")
optional func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int // tell table which section corresponds to section title/index (e.g. "B",1))
// Data manipulation - insert and delete support
// After a row has the minus or plus button invoked (based on the UITableViewCellEditingStyle for the cell), the dataSource must commit the change
// Not called for edit actions using UITableViewRowAction - the action's handler will be invoked instead
optional func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
// Data manipulation - reorder / moving support
optional func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath)
}
Ur code will works!!
This question is already answered but just want to make things a bit more Swifty.
Instead of writing protocols in UITableViewDelegate, UITableViewDataSource you can divide them using extensions this will help in organising the code. Adding protocol conformance is described in this page
for the above question, this can be confirmed to protocol using extension:
class GameList: UIViewController {
var aTableView:UITableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
aTableView.delegate = self
aTableView.dataSource = self
self.view.addSubview(aTableView)
}
}
extension GameList: UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath)
return cell
}
}
extension GameList: UITableViewDelegate{
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Row Clicked at \(indexPath.row)")
}
}