I have two Tableviews in one ViewController. But if I run this my code I will get this error on line let post = posts[indexPath.row]:
"Index out of range"
I think it has be something with the posts.count, but I can't figure it out. When I ran this code without the second Tableview Tableview_moremaps everything works just fine.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if comments.count == 0 {
self.tableView.setEmptyMessage("No comments yet!")
} else {
self.tableView.restore()
}
if posts.count == 0 {
self.Tableview_moremaps.setEmptyMessage("No other maps yet!")
}else{
self.Tableview_moremaps.restore()
}
if tableView == tableView {
return comments.count
}else{
return posts.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == self.tableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "Comment", for: indexPath) as? CommentTableViewCell
let comment = comments[indexPath.row]
cell?.comment = comment
cell?.delegate = self
return cell!
} else if tableView == Tableview_moremaps {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? MoremapsTableViewCell
cell?.Map_image.image = nil
let post = posts[indexPath.row]
cell?.post = post
cell?.delegate = self
return cell!
}
return UITableViewCell()
}
In numberOfRowsInSection, update the if-condition
if tableView === self.tableView {
return comments.count
} else {
return posts.count
}
Currently tableView == tableView is always true and returned value is comments count.
I think the problem is in the way you're using cellForRowAt
Try giving each tableview an identifier with tableView.accessibilityIdentifier = "myIdentifier"
and then cellForRowAtwould be like following:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView.accessibilityIdentifier == "myIdentifier" {
let cell = .....
...
...
...
...
...
you can try this
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == self.tableView {
return comments.count
}
else{
return posts.count
}
}
//Your this code is perfect.(no issue here)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == self.tableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "Comment", for: indexPath) as? CommentTableViewCell
let comment = comments[indexPath.row]
cell?.comment = comment
cell?.delegate = self
return cell!
} else if tableView == Tableview_moremaps {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? MoremapsTableViewCell
cell?.Map_image.image = nil
let post = posts[indexPath.row]
cell?.post = post
cell?.delegate = self
return cell!
}
return UITableViewCell()
}
Related
I have a problem handling 2 tables on the same screen. Every time he keeps crashing. Can someone help me?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: TableViewCellComunicazioni?
if tableView == self.tableViewNotifica {
cell = tableView.dequeueReusableCell(withIdentifier: "cellNotifica", for: indexPath) as? TableViewCellComunicazioni
let dataNotifica = structNotifica[indexPath.row].dateNotifica
let testoNotifica = structNotifica[indexPath.row].textNotifica
cell?.dateNotification.text = "\(date!)"
cell?.textNotification.text = "\(text!)"
return cell!
}
if tableView == self.tableViewInbox {
cell = tableView.dequeueReusableCell(withIdentifier: "cellInbox", for: indexPath) as? TableViewCellComunicazioni
let email = structInbox[indexPath.row].email
let messaggio = structInbox[indexPath.row].messaggio
let data = structInbox[indexPath.row].data
cell?.emailInbox.text = "\(email!)"
cell?.messaggioInbox.text = "\(message!)"
cell?.dataInbox.text = "\(date!)"
return cell!
}
return UITableViewCell()
}
This could be a probable fix for your problem:
Coding Example:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == self.tableViewNotifica {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellNotifica", for: indexPath) as? TableViewCellComunicazioni
let dataNotifica = structNotifica[indexPath.row].dateNotifica
let testoNotifica = structNotifica[indexPath.row].textNotifica
cell?.dateNotification.text = "\(date!)"
cell?.textNotification.text = "\(text!)"
return cell!
}
if tableView == self.tableViewInbox {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellInbox", for: indexPath) as? TableViewCellComunicazioni
let email = structInbox[indexPath.row].email
let messaggio = structInbox[indexPath.row].messaggio
let data = structInbox[indexPath.row].data
cell?.emailInbox.text = "\(email!)"
cell?.messaggioInbox.text = "\(message!)"
cell?.dataInbox.text = "\(date!)"
return cell!
}
return UITableViewCell()
}
And make sure that you have correct cell identifier for dequeuing.
extension ViewController : UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listmoviesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == moviesTableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "MovieTableViewCell", for: indexPath) as! MovieTableViewCell
cell.delegate = self
cell.setupCell(listmoviesArray[indexPath.row],indexPath: indexPath)
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "MovieTableViewCell2", for: indexPath) as! MovieTableViewCell
cell.delegate = self
cell.setupCell(listmoviesArray[indexPath.row],indexPath: indexPath)
return cell
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView == moviesTableView {
// Handle your selection for row.
} else {
//Handle your selection for row.
}
}
}
Above code produces the following output with 2 Tableview.
I have a tableview in my app.
I want to divide my cells into sections and give headlines.
Below I attach the picture (animals, birds headlines):
How do I do that in dynamic prototype cells?
My section data:
var sectionsData = [
"header",
"description",
"diagnoses",
"perscription",
"notes",
"addFaxHeadline",
"addFax",
"addEmailHeadline",
"addEmails",
"givePermissionHeadline",
"select answer"
]
Here is my cell at row function:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print ("indexPath: ", indexPath)
print ("indexPath: ", indexPath[0])
print ("-------")
if (sectionsData[indexPath[0]] == "header") {
let cell = tableView.dequeueReusableCell(withIdentifier: "headerCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "description") {
let cell = tableView.dequeueReusableCell(withIdentifier: "headerInfoCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "diagnoses") {
let cell = tableView.dequeueReusableCell(withIdentifier: "diagnosisCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "perscription") {
let cell = tableView.dequeueReusableCell(withIdentifier: "perscriptionCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "notes") {
let cell = tableView.dequeueReusableCell(withIdentifier: "notesCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "addFaxHeadline") {
let cell = tableView.dequeueReusableCell(withIdentifier: "addFaxCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "addFax") {
let cell = tableView.dequeueReusableCell(withIdentifier: "emailNameCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "addEmailHeadline") {
let cell = tableView.dequeueReusableCell(withIdentifier: "addEmailCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "addEmails") {
let cell = tableView.dequeueReusableCell(withIdentifier: "emailNameCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "givePermissionHeadline") {
let cell = tableView.dequeueReusableCell(withIdentifier: "permisionCell", for: indexPath)
return cell
} else if (sectionsData[indexPath[0]] == "select answer") {
let cell = tableView.dequeueReusableCell(withIdentifier: "selectAnswerCell", for: indexPath)
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "addFaxCell", for: indexPath)
// <<<< ???
return cell
}
and my numbers in row per section function:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if (sectionsData[section] == "header") {
print ("returning 1 ?")
return 1
} else if (sectionsData[section] == "description") {
return 1
} else if (sectionsData[section] == "diagnoses") {
//return visitSummary.diagnoses.count
return 2
} else if (sectionsData[section] == "perscription") {
//return visitSummary.prescriptions.count
return 2
} else if (sectionsData[section] == "notes") {
return 1
} else if (sectionsData[section] == "addFaxHeadline") {
return 1
} else if (sectionsData[section] == "addFax") {
return faxAdded.count
} else if (sectionsData[section] == "addEmailHeadline") {
return 1
} else if (sectionsData[section] == "addEmails") {
return emailsAdded.count
} else if (sectionsData[section] == "givePermissionHeadline") {
return 1
} else if (sectionsData[section] == "select answer") {
return 1
} else {
return 0
}
return 0
}
and my number of sections:
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
print ("sectionsData.count: ", sectionsData.count)
return sectionsData.count
}
All of the tutorials I find is for one section and I have a very amount of sections to show.
How would I divide it and give them headlines?
Let's clean up your code first:
enum Section: Int, CaseIterable {
case header = 0, description, diagnoses, prescription, notes, addFaxHeadline, addFax, addEmailHeadline, addEmails, givePermissionHeadline, selectAnswer
}
var sectionData: [Section] = [
.header,
.description,
.diagnoses
...
]
override func numberOfSections(in tableView: UITableView) -> Int {
return sectionsData.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection sectionIndex: Int) -> Int {
let section = sectionData[sectionIndex]
switch section {
case .header:
return 1
case .description:
return 1
// ...
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let section = sectionData[sectionIndex]
switch section {
case .header:
let cell = tableView.dequeueReusableCell(withIdentifier: "headerCell", for: indexPath)
return cell
// ... etc
}
}
Now, we can return section title just using a String:
override func tableView(_ tableView: UITableView, titleForHeaderInSection sectionIndex: Int) -> String? {
let section = sectionData[sectionIndex]
switch section {
case .header:
return "Header title"
case .description:
return "Description title"
default:
return nil
}
}
Another option is to use tableView(:viewForHeaderInSection:) and tableView(:heightForHeaderInSection:):
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = UIView()
let label = UILabel()
view.addSubview(label)
// setup constraints accordingly
// setup title of the label
return view
}
You can also use a custom reusable header which would be registered and dequeued in the same way cells are dequeued.
var sectionsData = [
"header",
"description",
"diagnoses",
"prescription",
"notes",
"addFaxHeadline",
"addFax",
"addEmailHeadline",
"addEmails",
"givePermissionHeadline",
"select answer"
] // Your Array
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? //*Data Source method for header title*
{
// Safe cast
if let sectionArray = sectionsData as? [String]
{
return sectionArray[section]
}
// A fail safe
return "No Header"
}
Now I see. You are misunderstanding how these methods work. They are called for every cell in every section.
So, if you return 4 in numberOfSections, the method numberOfRowsInSection will be called 4 times, and sectionIndex (now called section in latest versions) will have the current section (0 to 3 in our example).
Therefore, if you want to call the second section "Birds" and let all the others nil, your code will look like this:
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 1 {
return "Birds"
}
// Otherwise
return nil
}
Same thing for how many rows each section will have:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
// 1 row for the first section
return 1
} else if section == 1 {
// 3 rows for the second section
return 3
}
// 2 rows for every other section
return 2
}
And, finally, what cell will be used in each indexPath:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Figure out which row and section you are talking about
let row = indexPath.row
let section = indexPath.section
// Now you know where you are in the TableView, create the corresponding cell according to your project
// Start by dequeueing a custom cell using the identifier and forcing the cell to become your custom cell
let cell = tableView.dequeueReusableCell(withIdentifier: customCellID) as! CustomTableViewCell
// Do aditional setup to this cell
cell.textLabel?.text = "This is cell \(row) in section \(section)"
// Return the cell when you are ready
return cell
}
Remember Sulthan's suggestion to create an enum for your sections.
How can i create two UITableViews in one ViewController , I have one problem
The problem you need every single to Return is not within the condition and I have information for each Tableview
This message : " Missing return in a function expected to return 'Int' "
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == table_View {
return list.count
}
if tableView == table_View2 {
return list_2.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == table_View {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell_1") as! TableView_Cell
cell.la_view.text = list[indexPath.row]
cell.backgroundColor = UIColor(named: "Defeult")
return cell
}
if tableView == table_View2 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell_2") as! TableView_Cell
cell.la_view2.text = list_2[indexPath.row]
cell.backgroundColor = UIColor(named: "Defeult")
return cell
}
}
The isse is, that e.g. numberOfRowsInSection has to return something in any case. In your implementation, you have two if statements, and you, but only you know that this is sufficient, because tableView can only be any of the two. Unfortuantely, the compiler does not know this. Therefore, you could either do it a simple way:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == table_View {
return list.count
}
return list_2.count
}
Note: same applies to cellForRowAt function
Maybe better:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == table_View {
return list.count
} else if tableView == table_View2 {
return list_2.count
}
assertionFailure("Unexpected tableView")
return 0
}
As a quick solution (please make sure to read the rest of the answer), you could do it like this:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (tableView == table_View) ? list.count : list_2.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == table_View {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell_1") as! TableView_Cell
cell.la_view.text = list[indexPath.row]
cell.backgroundColor = UIColor(named: "Defeult")
return cell
}
if tableView == table_View2 { {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell_2") as! TableView_Cell
cell.la_view2.text = list_2[indexPath.row]
cell.backgroundColor = UIColor(named: "Defeult")
return cell
}
return UITableViewCell()
}
Important Tip:
Keep in mind that if you have to add two table views in the same view controller (which should be not a pretty cool idea), you could separate handling the dataSource and delegate for each table view in a different class (not the same view controller that contains the table views). Example:
In your view controller, set the dataSource as:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let table_View = UITableView()
let table_View2 = UITableView()
table_View.dataSource = SourceHandler1()
table_View2.dataSource = SourceHandler2()
}
}
Therefore, implement:
class SourceHandler1: NSObject, 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: "cell_1") as! TableView_Cell
cell.la_view.text = list[indexPath.row]
cell.backgroundColor = UIColor(named: "Defeult")
return cell
}
}
class SourceHandler2: NSObject, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
list_2.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell_2") as! TableView_Cell
cell.la_view2.text = list_2[indexPath.row]
cell.backgroundColor = UIColor(named: "Defeult")
return cell
}
}
That leads to avoid the issue of "massive" view controller and reduce the possibility of producing Spaghetti code.
change your code inside numberOfRowsInSection because of this delegate method required at least one Int value for a case. since you are using both value return in if condition, the error asking a value to return in else case.
So each case should return an Int value.
if tableView == table_View {
return list.count
}
else {
return list_2.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == table_View {
return list.count
}
if tableView == table_View2 {
return list_2.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == table_View {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell_1") as! TableView_Cell
cell.la_view.text = list[indexPath.row]
cell.backgroundColor = UIColor(named: "Defeult")
return cell
}
if tableView == table_View2 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell_2") as! TableView_Cell
cell.la_view2.text = list_2[indexPath.row]
cell.backgroundColor = UIColor(named: "Defeult")
return cell
}
return UITableViewCell()
}
I have the following code thus far.
var someData = [SomeData]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! Cell1
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as? Cell2
let someData = [indexPath.row]
//Set up labels etc.
return cell!
}
}
I need Cell1 which is a static cell and will always remain at indexPath 0 to be in a section called "Section1" for example & all of the Cell2's to be in a section called "Section2"
Other DataSource & Delegate Methods;
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
} else {
return someData.count
}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Section1" }
else {
return "Section2"
}
}
This returns me everything I need for the first section, however, when it comes to the second section (because of the code inside cellForRowAtIndex somewhere) section 2 contains Cell2 at indexPath 0.
Any help greatly appreciated.
Root cause:
In cellForRowAtIndexPath check for indexPath.section instead of indexPath.row
Fix:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! Cell1
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as? Cell2
let someData = [indexPath.row]
//Set up labels etc.
return cell!
}
}
In on of the git repositories I found pretty weird implementation of tableView cellForRowAt method.
The method have inside implementation of configureCell method. Isn't that wrong ? Let say.. for 1000 cells in table view - it is going to create this method about 1000 thousand times ? Can any one tell me if it is good way in swift or not ? I suppose it is totally wrong.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
func configureVehicleCell(_ object: CustomModel) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCellTableViewCell
if object.hasIssues {
cell.dvirImageView.isHidden = false
}
if object.cmv {
cell.cmvImageView.isHidden = false
}
cell.titleLabel.text = object.name
cell.subtitleLabel.text = object.someName
cell.tag = Int(object.id)
return cell
}
if lastUsedId == nil {
return configureCell(objectArray[(indexPath as NSIndexPath).row])
} else {
if (indexPath as NSIndexPath).section == 0 {
return configureCell(lastUsed!)
} else {
return configureCell(objectsArray[(indexPath as NSIndexPath).row])
}
}
}
In my opinion it should look like:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if lastUsedId == nil {
return configureCell(objectArray[(indexPath as NSIndexPath).row])
} else {
if (indexPath as NSIndexPath).section == 0 {
return configureCell(lastUsed!)
} else {
return configureCell(objectsArray[(indexPath as NSIndexPath).row])
}
}
}
func configureVehicleCell(_ object: CustomModel) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCellTableViewCell
if object.hasIssues {
cell.dvirImageView.isHidden = false
}
if object.cmv {
cell.cmvImageView.isHidden = false
}
cell.titleLabel.text = object.name
cell.subtitleLabel.text = object.someName
cell.tag = Int(object.id)
return cell
}