I'm doing a tutorial online right now building out a table of cells and I have a question. In the second function tableView below... what exactly is indexPath?
More importantly, how does the program know to update the value being passed through indexPath by one each time the function is called?
var rowNumber = 0
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 50
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let rowCell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "Cell")
rowCell.textLabel?.text = String(indexPath.row + 1)
return rowCell
}
TableView is divided into sections. Each section contains some number of rows. IndexPath contains information about which row in which section the function is asking about. Base on this numbers you are configuring the cell to display the data for given row. Application is executing this function when it needs to display a row. For example you scroll the tableview down and next cell will appear on the screen. To configure this cell with, for example, correct texts, it's asking you what should be displayed on row which is at this position (section and row).
Also, to improve performance you should create the cell using tableView.dequeueReusableCellWithIdentifier function instead of Cell's init.
Related
Hello,
i have created a UITableView in which it has two different cells DynamicFormCell and StaticFormCell, so the DynamicFormCell can be displayed number of times i have a data from a server telling me how many forms i need for the DynamicFormCell and the StaticFormCell is always the same and doesn't change so i am having difficulty giving different number of rows for each cell.i tried giving the two cell a tag of 0 and 1 respectively and used this code:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(tableView.tag == 0){
return 5//return five dynamic cells
}
if(tableView.tag == 1){
return 1//return one static cell
}
}
but this doesn't work and i also tried removing all the tags and if statements in the above code and just doing this return 5 this just gave me one DynamicFormCell and five StaticFormCells.
i also gave different classes for the two cells so i can assign them separately:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(indexPath.row == 0){
//firstRow make dynamic
let cell = tableView.dequeueReusableCell(withIdentifier: "DynamicFormsCell") as! DynamicFormsCell
return cell
}else{
//static form data
let cell = tableView.dequeueReusableCell(withIdentifier: "StaticFormsCell") as! StaticFormsCell
return cell
}
}
so my question is, is it possible to do this using table views and how can i do it? if not what other options do i have?
Yes it is possible to have multiple types of cell in single tableview. It has nothing to do with function
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
You should return there cells as,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (count of dynamic cells + count of static cells)
}
I assume, you only have to display static cells in the bottom. So if there are total 5 cells then 4 cells are dynamic and 5th cell would be static.
So code for, cellForRowAt indexPath: will be,
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(indexPath.row < (count for dynamic cells)){
//first 4 Rows make dynamic
let cell = tableView.dequeueReusableCell(withIdentifier: "DynamicFormsCell") as! DynamicFormsCell
return cell
}else{
//last row static form data
let cell = tableView.dequeueReusableCell(withIdentifier: "StaticFormsCell") as! StaticFormsCell
return cell
}
}
What you're doing right now is checking if the TableView's tag is 0 or 1. Which is not you want to do, since you're using only one TableView.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (amount of DynamicCellsYouWant + amount of StaticCellsYouWant)
}
The second part of your code only works when you want the first cell to be a DynamicFormsCell and the rest to be a StaticFormsCell.
I am interested in having a tableview for comments (something similar to instagram comments). So far, I have used a custom cell to set up a textView for comments in my set array, dataName. I was wondering how I could go about setting up a textfield and button on the last row of the tableview that would act as the place to input more comments. Do I need to create another customcell for this and implement this in cellForRowAt indexPath ?
var comments = ["I like this item","Where did you get this?", "I can't believe you found this!", "Hello", "Yay"]
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comments.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell
cell.commentView.text = dataName[indexPath.row]
cell.commentView.textColor = UIColor.lightGray
cell.commentView.isEditable = false
cell.commentView.isScrollEnabled = false
return cell
}
You can accomplish you desired effect by adding a view that contains a text field and a button as the footer view of the tableview. And when a new comment is added you will proceed to add the comment to the array, and reload the tableview or insertRow with animation.
You already have one cell prototype called "Cell". Just add another cell prototype called "Comment". Now you have two cell prototypes with two different identifiers. If you're on the last row, ask for the "Comment" cell prototype in your dequeue call.
I have a UITableView set up that works almost correctly, but if you scroll all the way up or down rather quickly, it shuffles the cells out of order. I set the label to reflect the index path and even when it starts out
[0,0] [0,1] [0,2] [0,3] [0,4] [0,5] [0,6]
After a quick few swipes up and down, the cells look like this
[0,1] [0,6] [0,2] [0,3] [0,4] [0,5] [0,0] [0,1]
My code is similar to the following
import UIKit
class TableViewController : UITableViewController {
var dates: [Date]?
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dates?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: tableViewCell.storyboardID, for: indexPath) as? tableViewCell else { fatalError() }
let date = dates?[indexPath.row]
cell.date = date
cell.delegate = self
cell.cellIndex = indexPath
return cell
}
}
From Apple docs on UITableView
When the table view asks the data source to configure a cell object for display, the data source can access the queued object by sending a dequeueReusableCellWithIdentifier: message to the table view, passing in a reuse identifier. The data source sets the content of the cell and any special properties before returning it. This reuse of cell objects is a performance enhancement because it eliminates the overhead of cell creation.
So in this case, you are storing the value of indexPath in the cell propeties, "cellIndex", which is reusable by other cells. Therefore, it will randomly show the value based on previously assigned in queue.
In order to encounter this, you can either store the value to array variable in viewController, so every time the datasource want to re-create the cells, it will get from the array consistently.
Secondly, you can directly assign the indexPath to the cell ui element such as UILabel.
cell.titleLabel.text = "\(indexPath)"
i have a uiviewcontroller, where i put a uitableview, with custom cells. Each of these cells, have inside a uicollectionview, with a horizontal scrolling.
I am working programmatically, so the tableviewcell is the delegate/datasource for collectionview.
My datasource structure, is an array of arrays.
My tableview, has, for every tableviewcell, a tableview section.Tableview datasource methods work without problem using the array. I am able to set up the tableview correctly.
Inside this cell, i want to display a collectionview, with horizontal scrolling.
The problem which i am encountering is i cannot assign datasource correctly using the arrays to collectionview. What happens is when i load the tableview, and scrolling down, i see duplicate records, so for example the first 3 rows are displayed correctly(in terms of data and number of items),but from the 4th row, for example, data is duplicated, so i see again the records for the first 3 rows, and of course this does not have to happen, because if i scroll on the 4th line(the collectionview on the 4th row) the app crashes due to an index problem out of range.
This is simple to understand: lets say i have 10 cells on the first row, and 5 on the 4th row/or section, as you prefer... Scrolling the 4th row'scollection view will cause the crash. This is because of the wrong data which is actually the same from the first row: instead, i have only 5 cells to render...
Tecnique i am using is pretty simple: give each tableviewcell's tag the actual indexpath.section. Then in the collectionview, use this tag to loop through array and then the indexpath.item to get the correct array.
Lets get a look into the code now:
Tableview
func numberOfSections(in tableView: UITableView) -> Int {
return DataManager.shared.datasource.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! DiarioTableViewCell
cell.tag = indexPath.section
return cell
}
TableviewCell
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: "cell")
collectionView.register(DiarioCVCell.self, forCellWithReuseIdentifier: "cellCV")
collectionView.delegate = self
collectionView.dataSource = self
DataManager.shared.istanzaCV = self.collectionView
addSubview(tableCellBG)
addSubview(collectionView)
setConstraints()
}
CollectionView
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let tvCell = collectionView.superview as! DiarioTableViewCell
return DataManager.shared.datasource[tvCell.tag].count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellCV", for: indexPath) as! DiarioCVCell
let celltag = (collectionView.superview as! DiarioTableViewCell).tag
cell.datasource = DataManager.shared.datasource[celltag][indexPath.item]
return cell
}
Notice i have read all the possibly related threads, even ashfurrow's article, and here on stack, but i was not able to find a solution.
Thanks for any help!
Try to reload your collection view when cell is showing. Cells are being reused to save memory so they are created only once - you are setting all data at init and it's staying there forever.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! DiarioTableViewCell
cell.tag = indexPath.section
cell.collectionView.reloadData()
cell.collectionView.collectionViewLayout.invalidateLayout() //just to be sure, maybe it's not necessary
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return titleList.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)->UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
cell.textLabel?.text = ("\(titleList[indexPath.row]) - \(timeDayList[indexPath.row]):\(timeHourList[indexPath.row]):\(timeMinuteList[indexPath.row]):\(timeSecondList[indexPath.row])")
return cell
}
I'd like to put the day, hour, minute, second into a new line. Is it possible or should I create another row for it.
You should create a custom UITableViewCell and
Either add labels to it and stack them one under the other
Or have a UITextView and show them each in a new line
I would personally go with the first option.