Use two UITableViews in one ViewController Swift - swift

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()
}

Related

SWIFT / UIKit Footer text displaying over dynamic tableViewCell

I have a UITableViewcontroller setup with just two cells. The footer text is displaying over the last cell.
Strangely I have other controllers with practically the same setup and code and where the footer is showing as expected.
I have tried tried changing the style group / inset etc.
Any ideas appreciated. Thanks
import UIKit
class LanguagesTableViewController: UITableViewController {
var checked = [Bool]()
var choices = ["English","French"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsMultipleSelection = false
let defaults = UserDefaults.standard
checked = defaults.array(forKey: "Language") as? [Bool] ?? [true, false]
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateSwitchState()
tableView.reloadData()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "languageChoiceCell", for: indexPath)
cell.textLabel?.text = choices[indexPath.row]
if !checked[indexPath.row] {
cell.accessoryType = .none
} else if checked[indexPath.row] {
cell.accessoryType = .checkmark
}
return cell
}
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 choices.count
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if checked[indexPath.row] {
tableView.selectRow(at: indexPath, animated: false, scrollPosition: UITableView.ScrollPosition.none)
}
}
override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return Constants.languagesFooterText
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// If we are selecting a row that is already checked we do nothing
guard !checked[indexPath.row] else { return }
// Reset all checked state.
checked = [Bool](repeating: false, count: choices.count)
// And set the current row to true.
checked[indexPath.row] = true
if let cell = tableView.cellForRow(at: indexPath) {
if cell.accessoryType == .checkmark {
cell.accessoryType = .none
checked[indexPath.row] = false
} else {
cell.accessoryType = .checkmark
checked[indexPath.row] = true
}
}
updateSwitchState()
}
// did ** DE ** Select
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
if cell.accessoryType == .checkmark {
cell.accessoryType = .none
checked[indexPath.row] = false
} else {
cell.accessoryType = .checkmark
checked[indexPath.row] = true
}
}
updateSwitchState()
}
func updateSwitchState() {
let defaults = UserDefaults.standard
defaults.set(checked, forKey: "Language")
}
}
You should set a height on your footer view. You can do this by calling tableView(:heightForFooterInSection:) and returning UITableView.automaticDimension.
As you are inheriting from UITableViewController you can do it in the following way
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
UITableView.automaticDimension
}
This will give you the following result:

Multiple prototype cells in UITableView

So I am trying to set up my UITableViewController that has a headerview, footerview, and then multiple prototype cells in between them. When I go to run my code on my simulator, only the hearderview and footerview are displaying and the prototype cells are not. Here is my code:
import UIKit
class UploadTrackTableViewController: UITableViewController {
var headerView: UploadHeader!
var footerView: UploadFooter!
override func viewDidLoad() {
super.viewDidLoad()
headerView = tableView.tableHeaderView as! UploadHeader
footerView = tableView.tableFooterView as! UploadFooter
}
}
extension UploadTrackTableViewController {
override func numberOfSections(in tableView: UITableView) -> Int {
return 4
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 1
} else if section == 1 {
return 1
} else if section == 2 {
return 1
} else {
return 1
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "trackTitleCell", for: indexPath) as! TrackTitleTableViewCell
return cell
} else if indexPath.section == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "genreCell", for: indexPath) as! SelectGenreTableViewCell
return cell
} else if indexPath.section == 2 {
let cell = tableView.dequeueReusableCell(withIdentifier: "featuredArtistCell", for: indexPath) as! AddFeaturedArtistTableViewCell
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "audioCell", for: indexPath) as! SelectAudioFileTableViewCell
return cell
}
}
}
And here is a screenshot of how my controller looks on interface builder.
And here is a screenshot of the simulator.
Not sure if setting this up as a tableview is the best option maybe I should go for a scrollview with this UI but I am not sure what would work best.
here is what the view controller looks like now.
You can use only 1 section and UITableView has delegate functions of
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return 'your custom view'
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return 'your custom view'
}
You can return you own custom view of header and footer here.
Happy coding. :)

"Index out of range" Error with two Tableviews in one Viewcontroller

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()
}

swift tableview change reloadData to insertRows

trying to change method to update the data, because with reloadData have lag
let oldIns = insertCounter
insertCounter += Int(INSERT_MESSAGES) // +40
var indexPaths = [IndexPath]()
for section in (oldIns..<insertCounter) {
indexPaths.append(IndexPath(row: 2, section: section))
}
tableView.beginUpdates()
tableView.insertRows(at: indexPaths, with: .automatic)
tableView.endUpdates()
but i have error
The number of sections contained in the table view after the update
(80) must be equal to the number of sections contained in the table
view before the update (40), plus or minus the number of sections
inserted or deleted (0 inserted, 0 deleted)
func numberOfSections(in tableView: UITableView) -> Int {
return min(insertCounter, Int(dbmessages.count))
}
//---------------------------------------------------------------------------------------------------------------------------------------------
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
//---------------------------------------------------------------------------------------------------------------------------------------------
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return RCMessages().sectionHeaderMargin
}
//---------------------------------------------------------------------------------------------------------------------------------------------
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return RCMessages().sectionFooterMargin
}
//---------------------------------------------------------------------------------------------------------------------------------------------
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
view.tintColor = UIColor.clear
}
//---------------------------------------------------------------------------------------------------------------------------------------------
func tableView(_ tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int) {
view.tintColor = UIColor.clear
}
//---------------------------------------------------------------------------------------------------------------------------------------------
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if (indexPath.row == 0) {
return RCSectionHeaderCell.height(indexPath, messagesView: self)
}
if (indexPath.row == 1) {
return RCBubbleHeaderCell.height(indexPath, messagesView: self)
}
if (indexPath.row == 2) {
let rcmessage = self.rcmessage(indexPath)
if (rcmessage.type == RC_TYPE_STATUS) { return RCStatusCell.height(indexPath, messagesView: self) }
if (rcmessage.type == RC_TYPE_TEXT) { return RCTextMessageCell.height(indexPath, messagesView: self) }
}
if (indexPath.row == 3) {
return RCBubbleFooterCell.height(indexPath, messagesView: self)
}
if (indexPath.row == 4) {
return RCSectionFooterCell.height(indexPath, messagesView: self)
}
return 0
}
//---------------------------------------------------------------------------------------------------------------------------------------------
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (indexPath.row == 0) {
let cell = tableView.dequeueReusableCell(withIdentifier: "RCSectionHeaderCell", for: indexPath) as! RCSectionHeaderCell
cell.bindData(indexPath, messagesView: self)
return cell
}
if (indexPath.row == 1) {
let cell = tableView.dequeueReusableCell(withIdentifier: "RCBubbleHeaderCell", for: indexPath) as! RCBubbleHeaderCell
cell.bindData(indexPath, messagesView: self)
return cell
}
if (indexPath.row == 2) {
let rcmessage = self.rcmessage(indexPath)
if (rcmessage.type == RC_TYPE_STATUS) {
let cell = tableView.dequeueReusableCell(withIdentifier: "RCStatusCell", for: indexPath) as! RCStatusCell
cell.bindData(indexPath, messagesView: self)
return cell
}
if (rcmessage.type == RC_TYPE_TEXT) {
let cell = tableView.dequeueReusableCell(withIdentifier: "RCTextMessageCell", for: indexPath) as! RCTextMessageCell
cell.bindData(indexPath, messagesView: self)
let numSections = self.tableView.numberOfSections
if numSections == 1 {
updateTableContentInset()
}
return cell
}
}
if (indexPath.row == 3) {
let cell = tableView.dequeueReusableCell(withIdentifier: "RCBubbleFooterCell", for: indexPath) as! RCBubbleFooterCell
cell.bindData(indexPath, messagesView: self)
return cell
}
if (indexPath.row == 4) {
let cell = tableView.dequeueReusableCell(withIdentifier: "RCSectionFooterCell", for: indexPath) as! RCSectionFooterCell
cell.bindData(indexPath, messagesView: self)
return cell
}
return UITableViewCell()
}
How can i correct insert new row in tableview?
Before does like this
insertCounter += Int(INSERT_MESSAGES)
tableView.reloadData()
It's a pretty simple code line:
tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .top)
This will insert the new row at the top of the table view. Then you would reload the data. This should fix the problem.

Sections in UITableView with Custom Cells

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!
}
}