UITableViewDataSource Functions? - ios

I'm a newbie in swift, I stumbuled upon these two functions that must be used when calling UiTableViewDataSource:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
I can see what each function do, but I can't understand their structures. Like what about the parameter that each one takes? Why don't we give any values of these parameters? How does it determine that what indexpath really is?

UITableView is the one responsible for calling them and picking the right index path according to what the table view needs or is visible at that time on the screen. Your only responsibility on the first one is to return the amount of elements that are in a given section and for the second one to dequeue the right cell and configure it with the data that corresponds to the index path that was passed in.

Related

Why does Xcode auto-fix create two methods with the same name `func tableView`?

I am new to swift programming language. I've seen that in creating table in Swift, you have to implement two methods in ViewController class that extends UITableViewDelegate, UITableViewDataSource. What I don't understand is, why does Xcode's auto-fix create two methods with the same name func tableView in this class?
Is this not going to create method overloading or cause bug down the road?
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: UITableView!
let dataArray = ["firt", "second", "third", "four", "five", "six"]
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let videoCell = tableView.dequeueReusableCell(withIdentifier: "video title", for: indexPath)
return videoCell
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.dataSource = self
tableView.dataSource = self
}
}
Even though they have the same function name tableView
They are very different functions.
They both conform to the UITableView delegate and based on its protocol method will affect different functionalities of the tableView.
didSelectRowAt
is not the same as
cellForRowAt
Did Select row at is only triggered when you obviously select a cell
Cell for row at is considered the 'main' tableView function, as this function populates your tableView data cells.
--EDIT
Based on Duncan C comment below.
" the name of your example function is not tableView, The name of of the function is tableView(_:cellForRowAt:) (The parameters are actually part of the function's name, or rather it's function "signature.") "
This is an excellent way to describe the answer.
Edit 2----
Furthermore,
This is very common among programming in swift. The most direct example would be collectionView. It uses almost the identical naming convention.
cellForRowAt
and
didSelectRowAt
There are many other delegate methods that you will encounter with the same situation as you describe in your question.
They are actually different methods. Each one overrides some properties of your TableView.
Just pay attention to the parameters of each function.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
This method creates and configures an appropriate cell for the given index path.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
Here you decide what happens when cell rows are clicked (go to another view, display some content, etc)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
Returns the number of rows each section of your TableView should have.
And so on. There are many others methods that you can use for different reasons. To see which methods you can override, type tableView on your Xcode and see the autocomplete options.
That is called method overloading. Swift designers chose to follow this way of writing functions to make it easier for the programmers to find all related tableView functions. That tableView has many more functions like:
a one that has heightForRowAt and willDisplayCell in its parameters. Swift utilizes heavily a pattern called Delegate Pattern, the framework calls these tableView functions when you assign your ViewController as a delegate.

Swift UITableView (no sections) filter to table view with sections

I have a list of custom objects [Species] displayed in the table view, which is sorted alphabetically. Table has one section with no headers, it's one continuous list.
What I would like to achieve is, when a user selects the option to sort the data "by country", to do the following:
to sort array to find out how many sections I will need by - "Species.country"
to create sections with header titles of the country
to sort countries (Sections) alphabetically
reload table view to display sections
remove sections on the reversed action (sort entire list A-Z)
Is it possible to create dynamically sections when filtering/sorting? Can you please point me the right direction? Many thanks
A.
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.genusArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: CustomMenuCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomMenuCell
//genusArr is type of [Species]
let genus = self.genusArr[indexPath.row]
cell.populate(with: genus)
return cell
}
It's all in your data model. You always have sections with rows. But you only have one section. Modify your data model and your data source methods to always support multiple sections. Just sometimes this data model will only have one section.
Update your data model to be an array of dictionary where the dictionary contains a title and an array. The top level array is your sections (may just be one at times). The inner arrays (in each dictionary) are the rows of each section. Or define a struct with a title and array instead of using a dictionary.
With this in place, and your table view data source and delegate methods written to always work with multiple sections, your table will also work just fine when you happen to have just one section worth of data.
Now it's just a matter of populating your array of dictionary as needed depending on how you wish to organize the data for display.
To answer the question of section headers. You could try using:
tableView(_ tableView: UITableView, titleForHeaderInSection section: Int)
or
tableView(tableView: UITableView, viewForHeaderInSection section: Int)

Push to the next table view

I have two table views containing data, the first one is completely okay. I want to push from this table view to another view to another table view, but I cannot set the rows for it. How can I navigate through a multidimensional array in
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
Basically, my problem is about setting the right text for a row and getting from a multidimensional array :) (P.S. I have a different number of Strings in each subarray)
You just need to check on which tableView you are setting the values.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
if tableView == myFirstTableView {
// put the data for the first tableView
} else {
// put the data for the second tableview
}
}

Ignore first item in array in UITableView - Swift

How can I ignore the first item in an array when display in a TableView?
What I want is to ignore the first item when presented in a UITableView, I DON'T want remove it just not show it in the TableView.
The following code show all items from the array in the TableView.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lists.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "listCell", for: indexPath)
let data = lists[indexPath.row]
cell.textLabel!.text = data.listName
return cell
}
Your data source methods are using lists as their basis, and you don't want to do anything that mess that up. The numberOfRowsInSection and the cellForRowAt need to stay in sync.
I can think of two possibilities:
Keep the real model elsewhere, and keep in lists only the part of the model that you want to include in the table.
Or (a whole different approach) implement heightForRowAt to give the undesired row a zero height.

Changing index of a cell

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!

Resources