I have a subclass of UITableViewController where I have 2 properties that will defines the number of sections and number of rows in section
It seems the the delegate methods numberOfSectionsInTableView(tableView: UITableView) -> Int get called before viewDidLoad() which is where I am initialising the 2 properties at currently. Therefore the 2 properties does not have any value when the delegate methods get called.
Where should I initialise these 2 value such that it will be ready for the delegate method.
It shouldn't be calling that delegate func before viewDidLoad.
I just tested the following code:
override func viewDidLoad() {
super.viewDidLoad()
print("viewDidLoad")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("whatt????")
}
When I run my app, this prints:
viewDidLoad
whatt???
whatt???
whatt???
I would test that your variables are properly initialized. If they are, you should have no problem using them to define how many rows/sections you have.
Related
I'm trying to adopt the new iOS 13 UITableViewDiffableDataSource and I've hit a snag; I can't work how to implement
func sectionIndexTitles(for tableView: UITableView) -> [String]?
That's a data source method, not a delegate method. So, now that the data source is the UITableViewDiffableDataSource, it needs to implement that method. But it doesn't.
I tried subclassing UITableViewDiffableDataSource and adding an implementation of sectionIndexTitles, but my implementation was never called:
class MyDataSource : UITableViewDiffableDataSource<String,String> {
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return self.snapshot().sectionIdentifiers // not called
}
}
Has anyone solved this one? I'll file it as a bug just in case.
You need to subclass UITableViewDiffableDataSource and then overwrite
func sectionIndexTitles(for tableView: UITableView)
by yourself.
To enable the index' functionality though, you have to also overwrite
func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int)
Here is an example how I implemented it:
import UIKit
import MediaPlayer
class TableViewDiffableDataSource: UITableViewDiffableDataSource<String, MPMediaItemCollection> {
var sectionTitles = [String]()
override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return sectionTitles
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionTitles[section]
}
override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
return sectionTitles.firstIndex(of: title) ?? 0
}
}
Credits go to Steve Breen, who led me in the right direction.
After you init self.dataSource to UITableViewDiffableDataSource (which sets itself to the tableView.dataSource) set the tableView.dataSource back to self, i.e. the UITableViewController subclass. Now in your numberOfSectionsInTableView and numberOfRowsInSection methods forward those to self.dataSource and return its info (this is the composition pattern). Now your UITableViewController just implements its section titles as normal since it is the table's data source.
I believe UITableViewDiffableDataSource should not be setting itself as the dataSource if one is already set but I guess they designed it to work in the least error prone way because with UITableViewController added to a storyboard it's set as the dataSource and delegate by default.
I have a UITableViewController with static cells, and when I run the app and click on the button that leads to it, I get a SIGABRT signal in AppDelegate.
I tried to find unused outlets, but it didn't work.
Here is the Console Log:
The UITableViewController Code:
import UIKit
import os.log
class SettingsTableViewController: UITableViewController {
// MARK: Properties
#IBOutlet weak var noteDisplayKindSwitch: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
notedisplayKindSwitch.setOn(Settings._displaynotesAsNames, animated: false)
}
#IBAction func ChangeNoteDisplayKind(_ sender: UISwitch) {
Settings._displayNotesAsNames = sender.isOn
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
}
The UITableViewController in the Storyboard:
The Connections:
What am I doing wrong?
Thanks.
You are using static cells. There is no reason to implement numberOfSections and numberOfRowsInSection because those are specified by the static layout in the Storyboard.
Because you have implemented:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
you are telling iOS that every section has 3 rows, which is a lie. So iOS tries to access the second row of your first section and crashes with array index out of range because that section has just 1 row.
So, delete the implementations of numberOfSections and numberOfRowsInSection and you should be good to go.
Read the error message, and please do not post images of code.
Somewhere in your tableView:cellforRowAtIndexPath method, there's an empty array but you're trying to access the element at index 1.
I created a table view controller and from drop down in Xcode selected content to be static cells. I placed buttons in those cells but when I run I get an empty table. I didn't enter any code in the TableViewController class as I thought this was not needed.
How can this be fixed?
This is the way it looks in Xcode and then when it runs in simulator:
You may miss some stuff:
1) Check whether your tableView has set the class named as that on in your code. You find the input field to type the name of the class in Attributes inspector of the tableView.
2) Check whether you implemented methods, that comform to UITableViewDelegate and UITableViewDataSource Protocol.
func numberOfSections(in tableView: UITableView) -> Int {
// I see you only have 1 section now
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//you should return appropriate number
return 3
}
3) Check whether your table is properly connected from Storyboard to your UIViewController =>
if your tableView is inside UIViewController, check whether you set delegate and datasource for tableView in your controller (CTRL drag from table to FileOwner - rounded yellow icon in storyboard scene frame.)
using static cells, it's not necessary to implement numberOfSections and numberOfRowsInSection. By default, at runtime you get the static cell(s) as implemented in the designer.
However, if you do implement these methods and, by error, return 0: No cell is shown at runtime.
Following up on the answer above:
1) Make sure the tableview is hooked up as both the datasource and delegate.
2) Make sure these two lines of code are modified to your table. Default is to return 0 (an empty table) so you need to modify them accordingly. You can also choose to comment them out completely (what I did).
func numberOfSections(in tableView: UITableView) -> Int {
// I see you only have 1 section now
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//you should return appropriate number
return 3
}
I have a table view controller that needs to be updated through a delegate call. I have set the datasource and delegate and on initial load of the tableview, all works as expected. I have a delegate method that gets called after a datasource update. The delegate calls a refresh method in the table view controller class which calls .reloadData()
When reloadData is called, numberOfRowsInSection is called and accurately returns the number of rows, however cellForRowAtIndexPath never gets called.
In this particular case, numberOfRowsInSection returns 2, therefore cellForRowAtIndexPath should be called twice but it's called zero times.
On initial load everything is fine. It's only when reloadData is called taht cellForRowAtIndexPath is ignored. I have done this same thing many times in Obj-C without any weirdness. Are there any known issues with this in Swift?
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(LayerMenuCell.reuseId) as! LayerMenuCell
// ....
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(layerEntries?.count)
return (layerEntries?.count)!
}
func refresh() {
self.layersTableView.reloadData()
}
Thanks!
Try setting the delegate and dataSource of your UITableView:
myTable.delegate = self
myTable.dataSource = self
As specified in the Documentation:
A functioning table view requires three table view data source
methods.
func numberOfSectionsInTableView(tableView: UITableView) -> Int
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
MAake sure you implement the above three delegate methods and they return some values other than nil or 0.
There is a chance that cell height could be 0/ table height is 0 in both the cases cell for row method will not get called.
Also make sure you set the delegate properly and call the reloadData method on main thread. More on here
Things you need to check when Tableview is not working as expected:
1. Setting the delegate and datasource through storyboard or by code.
self.tableView.delegate = self
self.tableView.dataSource = self
2.check if tableView in storyboard is connected to tableView outlet.
3.check numberOfRowsInSection and numberOfSectionsInT‌​ableView returning the correct values.
4.check if the methods is written properly.
5.add the delegate and datasource after UIViewController.
<UITableViewDelegate , UITableViewDataSource>
this will help you if you are missing any thing.
I'm starting to work with UITableViews and can't seem to find out how to change the position of a cell with code. Changing the position in the storyboard is straightforward enough but I need to be able to do it in swift.
TLDR;
Update your data. i.e. swap(&arr[2], &arr[3]).
Call the tableView's reloadData() method to reflect the changes to your data.
Long answer
An instance of UITableView works by checking its data source (UITableViewDataSource) for the information it needs. This includes the number of sections and rows, as well as the instance of UITableViewCell that the table view is to use. These are defined by the following UITableViewDataSource delegate methods:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int;
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int;
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell;
Usually, you would base the former two on some data you have, likely an Array or similar container. For example, if your tableView displayed data from an Array named fruitArray (which contained names of different fruit - a list of strings), then you might have something like the following:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Our array is one dimensional, so only need one section.
// If you have an array of arrays for example, you could set this using the number of elements of your child arrays
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Number of fruits in our array
return fruitArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("yourCellId") // Set this in Interface Builder
cell.textLabel?.text = fruitArray[indexPath.row]
return cell
}
Then, you can see that the answer to your question becomes simple! Since the contents of a given cell are based upon fruitArray, all you need to do is update your array. But how do you get the tableView to "recheck" its dataSource? Well, you use the reloadData method, like so:
swap(&fruitArray[2], &fruitArray[3])
tableView.reloadData()
This then triggers the tableView to "recheck" its dataSource, hence causing your data swap to appear on the screen!
If you'd like the user to be able to swap the positions of the cells, you can use the following UITableViewDelegate (not UITableViewDataSource) delegate method:
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool
Have a look at this article for more info. You can also view Apple's documentation on UITableView, UITableViewDataSource, and UITableViewDelegate for further detail.
Hope this helps!