Setting a UITableView data source and delegate in separate file - swift - ios

If I would like to have the same basic UITableView appearing in two different scenes, is it a good idea to use one datasource and delegate location for both tables?
I wanted to try this, but when I select the table view in IB and try to drag the line to a custom class of UITableView file, or even to another custom view controller, it will not connect. It only seems possible to make the current View Controller into the table's datasource and delegate(?).
I'm wondering if this is at least similar to this question, but even if it is, how is this done in swift (and perhaps there is a new way to do this).

Swift 4.1. You can create separate class and inherit it from UITableViewDataSource and UITableViewDelegate class. Here, I am implementing UITableViewDataSource() methods in DataSource class. You also need to confirm NSObject so that we don’t have to fiddle with the #objc and #class keywords because UITableViewDataSource is an Objective-C protocol.
import Foundation
import UIKit
class DataSource: NSObject, UITableViewDataSource {
var formData: [FormData]? = nil
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.formData?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let label = cell?.contentView.viewWithTag(100) as? UILabel
let type = self.formData![indexPath.row]
label?.text = type.placeHolder
return cell!
}
}
Now, We will set DataSource to UITableView. If we crate separate class then we have to pass data to DataSource class.
class ViewController: UIViewController {
#IBOutlet weak var tblView: UITableView!
var formData: [FormData]? = nil
var dataSource = DataSource()
override func viewDidLoad() {
super.viewDidLoad()
formData = FormData.array
dataSource.formData = formData // Pass data to DataSource class
tblView.dataSource = dataSource // Setting DataSource
}
}
In similar way you can implement UITableViewDelegate in separate class. The other way to separate DataSource and Delegate is by creating extension of your viewController. Even you can crate separate class where you can only define extensions for your view controller. In you define extension then you don't need to pass data.
class ViewController: UIViewController {
#IBOutlet weak var tblView: UITableView!
var formData: [FormData]? = nil
override func viewDidLoad() {
super.viewDidLoad()
formData = FormData.array
tblView.dataSource = self
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.formData?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let label = cell?.contentView.viewWithTag(100) as? UILabel
let type = self.formData![indexPath.row]
label?.text = type.placeHolder
label?.backgroundColor = UIColor.gray
return cell!
}
}

Here is a code example showing different Datasource and delegates for UITableView.
Code in Swift
import UIKit
// MARK: Cell
class ItemCell: UITableViewCell{
var label: UILabel!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 20))
label.textColor = .black
label.backgroundColor = .yellow
contentView.addSubview(label)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: Main View Controller
class BlueViewController: UIViewController{
var tableView: UITableView!
var myDataSourse: MyTVDataSource!
var myDelegate: MyTVDelegate!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .blue
tableView = UITableView()
myDataSourse = MyTVDataSource(tableView: tableView)
myDelegate = MyTVDelegate()
myDelegate.presentingController = self
tableView.dataSource = myDataSourse
tableView.delegate = myDelegate
tableView.register(ItemCell.self, forCellReuseIdentifier: "Cell")
self.view.addSubview(tableView)
self.tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0)
])
}
}
extension BlueViewController: BluePresenting{
func currentSelected(_ indexPath: IndexPath) {
print(indexPath)
}
}
// MARK: TableViewDelegate
protocol BluePresenting: class {
func currentSelected(_ indexPath: IndexPath)
}
class MyTVDelegate: NSObject,UITableViewDelegate{
var presentingController: BluePresenting?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
presentingController?.currentSelected(indexPath)
}
}
// MARK: TableView DataSource
class MyTVDataSource: NSObject, UITableViewDataSource{
private var tableView: UITableView
private var items = ["Item 1","item 2","item 3","Item 4"]
init(tableView: UITableView) {
self.tableView = tableView
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = ItemCell(style: .default, reuseIdentifier: "Cell")
cell.label.text = items[indexPath.row]
return cell
}
}

You can implement a custom class object, and implement the UITableViewDataSource methods for this class.
#interface MyDataSource : NSObject <UITableViewDataSource>
//...
#end
And then, the UITableView has properties, delegate and dataSource.
Assign right objects to those properties.
MyDataSource ds = ... ///< Initialize the dataSource object.
self.tableView.dataSource = ds; ///< Let ds be the dataSource of `self.tableView`
self.tableView.delegate = .... ///< Assign the delegate, generally it is `self`.

Each Tableview should have its own Tableview controller. This is in accordance with the Model View Controller Design Pattern.
If the data in the the two tables are the same, you could have a common class serve as the dataSource.

Related

Correct way to use UITableViewDataSource and Custom UITableViewCell Delegate

I'm working on moving the UITableViewDataSource outside of the UITableViewController. However I have some custom cells that have their own delegates, which then call on the tableView to reload.
I'm not sure what the correct way of handling this is. Here's what I have:
final class MyTableViewController: UITableViewController {
lazy var myTableViewDataSource: MyTableViewDataSource = { MyTableViewDataSource(myProperty: MyProperty) }()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = myTableViewDataSource
}
}
Cell
protocol MyTableViewCellDelegate: AnyObject {
func doSomething(_ cell: MyTableViewCellDelegate, indexPath: IndexPath, text: String)
}
final class MyTableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var packageSizeTextField: UITextField!
weak var delegate: MyTableViewCellDelegate?
var indexPath = IndexPath()
override func awakeFromNib() {
super.awakeFromNib()
}
func configureCell() {
// configureCell...
}
func textFieldDidChangeSelection(_ textField: UITextField) {
print(#function)
delegate?.doSomething(self, indexPath: indexPath, text: textField.text ?? "")
}
}
DataSource
final class MyTableViewDataSource: NSObject, UITableViewDataSource {
var myProperty: MyProperty!
init(myProperty: MyProperty) {
self.myProperty = myProperty
}
// ...
func doSomething(_ cell: MyTableViewCell, indexPath: IndexPath, text: String) {
// ...
tableView.performBatchUpdates({
tableView.reloadRows(at: [indexPath], with: .automatic)
})
// ERROR - tableView doesn't exist
}
}
My question is, how do I gain access to the tableView that this class is providing the source for? Is it as simple as adding a reference to the tableView like this?
var tableView: UITableView
var myProperty: MyProperty!
init(myProperty: MyProperty, tableView: UITableView) {
self.myProperty = myProperty
self.tableView = tableView
}
One option is to have your MyTableViewController conform to your MyTableViewCellDelegate and then set the controller as the delegate in cellForRowAt in your dataSource class.
However, you may be much better off using a closure.
Get rid of your delegate and indexPath properties in your cell, and add a closure property:
final class MyTableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var packageSizeTextField: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
configureCell()
}
func configureCell() {
// configureCell...
packageSizeTextField.delegate = self
}
var changeClosure: ((String, UITableViewCell)->())?
func textFieldDidChangeSelection(_ textField: UITextField) {
print(#function)
changeClosure?(textField.text ?? "", self)
// delegate?.doSomething(self, indexPath: indexPath, text: textField.text ?? "")
}
}
Now, in your dataSource class, set the closure:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "mtvc", for: indexPath) as! MyTableViewCell
c.packageSizeTextField.text = myData[indexPath.row]
c.changeClosure = { [weak self, weak tableView] str, c in
guard let self = self,
let tableView = tableView,
let pth = tableView.indexPath(for: c)
else {
return
}
// update our data
self.myData[pth.row] = str
// do something with the tableView
//tableView.reloadData()
}
return c
}
Note that as you have your code written, the first tap in the textField will not appear to do anything, because textFieldDidChangeSelection will be called immediately.
Edit
Here's a complete example that can be run without any Storyboard connections.
The cell creates a label and a text field, arranging them in a vertical stack view.
Row Zero will have the text field hidden and its label text will be set to the concatenated strings from myData.
The rest of the rows will have the label hidden.
The closure will be called on .editingChanged (instead of textFieldDidChangeSelection) so it is not called when editing begins.
Also implements row deletion for demonstration purposes.
The first row will be reloaded when text is changed in any row's text field, and when a row is deleted.
Cell Class
final class MyTableViewCell: UITableViewCell, UITextFieldDelegate {
var testLabel = UILabel()
var packageSizeTextField = UITextField()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
configureCell()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configureCell()
}
func configureCell() {
// configureCell...
let stack = UIStackView()
stack.axis = .vertical
stack.translatesAutoresizingMaskIntoConstraints = false
testLabel.numberOfLines = 0
testLabel.backgroundColor = .yellow
packageSizeTextField.borderStyle = .roundedRect
stack.addArrangedSubview(testLabel)
stack.addArrangedSubview(packageSizeTextField)
contentView.addSubview(stack)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: g.topAnchor),
stack.leadingAnchor.constraint(equalTo: g.leadingAnchor),
stack.trailingAnchor.constraint(equalTo: g.trailingAnchor),
stack.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
packageSizeTextField.addTarget(self, action: #selector(textChanged(_:)), for: .editingChanged)
}
var changeClosure: ((String, UITableViewCell)->())?
#objc func textChanged(_ v: UITextField) -> Void {
print(#function)
changeClosure?(v.text ?? "", self)
}
}
TableView Controller Class
class MyTableViewController: UITableViewController {
lazy var myTableViewDataSource: MyTableViewDataSource = {
MyTableViewDataSource()
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
tableView.register(MyTableViewCell.self, forCellReuseIdentifier: "mtvc")
tableView.dataSource = myTableViewDataSource
}
}
TableView DataSource Class
final class MyTableViewDataSource: NSObject, UITableViewDataSource {
var myData: [String] = [
" ",
"One",
"Two",
"Three",
"Four",
"Five",
"Six",
"Seven",
"Eight",
]
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
myData.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return indexPath.row != 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "mtvc", for: indexPath) as! MyTableViewCell
c.testLabel.isHidden = indexPath.row != 0
c.packageSizeTextField.isHidden = indexPath.row == 0
if indexPath.row == 0 {
myData[0] = myData.dropFirst().joined(separator: " : ")
c.testLabel.text = myData[indexPath.row]
} else {
c.packageSizeTextField.text = myData[indexPath.row]
}
c.changeClosure = { [weak self, weak tableView] str, c in
guard let self = self,
let tableView = tableView,
let pth = tableView.indexPath(for: c)
else {
return
}
// update our data
self.myData[pth.row] = str
// do something with the tableView
// such as reload the first row (row Zero)
tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
}
return c
}
}
Edit 2
There is a lot to discuss which goes beyond the scope of your question, but briefly...
First, as a general rule Classes should be as independent as possible.
your Cell should only handle its elements
your Data Source should only manage the data (and, of course, the necessary funds like returning cells, handling Edit commits, etc)
your TableViewController should, as might be expected, control the tableView
If you are only manipulating the data and wanting to reload specific rows, it's not that big of a deal for your DataSource class to get a reference to the tableView.
But, what if you need to do more than that? For example:
You don't want your Cell or DataSource class to act on the button tap and do something like pushing a new controller onto a nav stack.
To use the protocol / delegate pattern, you can "pass a delegate reference" through the classes.
Here's an example (with just minimum code)...
Two protocols - one for text change, one for button tap:
protocol MyTextChangeDelegate: AnyObject {
func cellTextChanged(_ cell: UITableViewCell)
}
protocol MyButtonTapDelegate: AnyObject {
func cellButtonTapped(_ cell: UITableViewCell)
}
The controller class, which conforms to MyButtonTapDelegate:
class TheTableViewController: UITableViewController, MyButtonTapDelegate {
lazy var myTableViewDataSource: TheTableViewDataSource = {
TheTableViewDataSource()
}()
override func viewDidLoad() {
super.viewDidLoad()
// assign custom delegate to dataSource instance
myTableViewDataSource.theButtonTapDelegate = self
tableView.dataSource = myTableViewDataSource
}
// delegate func
func cellButtonTapped(_ cell: UITableViewCell) {
// do something
}
}
The data source class, which conforms to MyTextChangeDelegate and has a reference to MyButtonTapDelegate to "pass to the cell":
final class TheTableViewDataSource: NSObject, UITableViewDataSource, MyTextChangeDelegate {
weak var theButtonTapDelegate: MyButtonTapDelegate?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! theCell
// assign custom delegate to cell instance
c.theTextChangeDelegate = self
c.theButtonTapDelegate = self.theButtonTapDelegate
return c
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func cellTextChanged(_ cell: UITableViewCell) {
// update the data
}
}
and the Cell class, which will call the MyTextChangeDelegate (the data source class) on text change, and the MyButtonTapDelegate (the controller class) when the button is tapped:
final class theCell: UITableViewCell, UITextFieldDelegate {
weak var theTextChangeDelegate: MyTextChangeDelegate?
weak var theButtonTapDelegate: MyButtonTapDelegate?
func textFieldDidChangeSelection(_ textField: UITextField) {
theTextChangeDelegate?.cellTextChanged(self)
}
func buttonTapped() {
theButtonTapDelegate?.cellButtonTapped(self)
}
}
So, having said all that...
Speaking in the abstract can be difficult. For your specific implementation, you may be digging yourself into a hole.
You mention "how to use a containerView / segmented control to switch between controllers" ... It might be better to create a "data manager" class, rather than a "Data Source" class.
Also, with a little searching for Swift Closure vs Delegate you can find a lot of discussion stating that Closures are the preferred approach these days.
I put a project up on GitHub showing the two methods. The functionality is identical --- one approach uses Closures and the other uses Protocol/Delegate pattern. You can take a look and dig through the code (tried to keep it straight-forward) to see which would work better for you.
https://github.com/DonMag/DelegatesAndClosures

Swift - adding cells in UITableView

I have a UITableView and I am trying to add some content (Whish) in my case. I have set up everything programmatically but somehow adding cells is not working for me.
This is my UITableView and custom Wish Class for my data:
import UIKit
class WhishlistTableViewController: UITableViewController {
public var wishList : [Wish]?
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(WhishCell.self, forCellReuseIdentifier: WhishCell.reuseID)
self.wishList?.append(Wish(withWishName: "Test"))
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return wishList?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: WhishCell.reuseID, for: indexPath)
let currentWish = self.wishList![indexPath.row]
cell.textLabel?.text = currentWish.wishName
return cell
}
}
class Wish: NSObject {
public var wishName : String?
init(withWishName name: String) {
super.init()
wishName = name
}
}
And this is how I am creating the WishlistTableView inside my other UIViewController:
let theTableView: WhishlistTableViewController = {
let v = WhishlistTableViewController()
v.view.layer.masksToBounds = true
v.view.layer.borderColor = UIColor.white.cgColor
v.view.layer.borderWidth = 2.0
v.view.translatesAutoresizingMaskIntoConstraints = false
return v
}()
and its constraints:
theTableView.view.topAnchor.constraint(equalTo: wishlistView.topAnchor, constant: 180.0),
theTableView.view.bottomAnchor.constraint(equalTo: wishlistView.bottomAnchor, constant: 0),
theTableView.view.leadingAnchor.constraint(equalTo: wishlistView.safeAreaLayoutGuide.leadingAnchor, constant: 30.0),
theTableView.view.trailingAnchor.constraint(equalTo: wishlistView.safeAreaLayoutGuide.trailingAnchor, constant: -30.0),
The TableView appears just fine with its constraints but adding cells is not working and I have no idea why.. Grateful for every help :)
Replace
public var wishList : [Wish]?
with
public var wishList = [Wish]()
As you never init wishList so adding item here wishList?.append won't do anything
self.wishList.append(Wish(withWishName: "Test"))
Also remove this as it's the default
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

TableView doesn't show my Data from Realm

i have one problem: my tableView doesn't show any data from realm database. But i guess the problem is in cell.
class ClientViewController: UITableViewController {
#IBOutlet var table: UITableView!
var clientsInfo: Results<Client> {
get {
return realm.objects(Client.self)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(myCell.self, forCellReuseIdentifier: "cell")
table.delegate = self
table.dataSource = self
table.reloadData()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: myCell.reuseIdentifier, for: indexPath) as! myCell
let info=clientsInfo[indexPath.row]
cell.todoLabel.text = info.firstName
return cell
}
}
class myCell: UITableViewCell {
#IBOutlet weak var label: UILabel!
static let reuseIdentifier = "cell"
#IBOutlet weak var todoLabel: UILabel!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
}
There is a few things wrong in your code,
First:
You're using a UITableViewController, by default this already has a tableView, so you don't need to declare a new tableView like you did
#IBOutlet var table: UITableView!
Second:
In you viewDidLoad you're registering the cell for the default tableView and using your declared table for dataSource and delegate, this won't work. I recommend you remove the table you create and use the default so your viewDidLoad would look like this:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(myCell.self, forCellReuseIdentifier: "cell")
self.tableView.delegate = self
self.tableView.dataSource = self
}
and Third:
you're missing the implementation of :
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
In this code you forgot to implement tableView(numberOfRowsInSection:) method, so it returns 0 by default and your tableview(cellForRowAt:) method never called.
AND
Maybe this is not about your question but;
You used UITableView variable(name is table) in UITableView. It's unneccessary, because UITableView already has a UITableView(name is tableView). And you'have registered your cell into tableView, but it seems you try to dequeue this cell from table.

Swift - UITableView doesn't work with delegates

The problem is that UITableView.dataSource works fine with extensions, but does not work with delegates.
I created new project and added just one UITableView to the storyboard.
Here is the code using extension:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
// tableView.dataSource = Delegate()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
extension ViewController: UITableViewDataSource {
//class Delegate: NSObject, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(1) // called several times
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print(2) // called
var cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "UITableViewCell")
}
return cell!
}
}
Using delegate:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// tableView.dataSource = self
tableView.dataSource = Delegate()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
//extension ViewController: UITableViewDataSource {
class Delegate: NSObject, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(1) // called several times
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print(2) // doesn't called
var cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "UITableViewCell")
}
return cell!
}
}
Has anyone encountered such a problem?
You need to store your Delegate object in you view controller. The reason for this is that dataSource variable of UITableView is a weak variable (so as to prevent retain cycles) - it means that if it isn't stored somewhere in a strong variable, it will immediately get deallocated.
tableView.dataSource = Delegate()
Here you assign new instance of Delegate to a weak variable and nowhere else. Do something like this
class ViewController: UIViewController {
var delegate = Delegate()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self.delegate
}
The dataSource and delegate are both weak. So your Delegate() object with no strong reference will be released after viewDidLoad().
If you want to use delegate this way, just var delegate: Delegate! in your ViewController. Then in the viewDidLoad() :
self.delegate = Delegate()
tableView.delegate = self.delegate

Why is my UITableView not showing?

I can't figure out why my TableView is not showing. Its probably something stupid but all of my MenuItemStruct's are complete and not null and my table view seems set up correctly to me. There are no errors, but my cellForRowAt method is not getting called. Please help?
class MenuViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var menuNavigationDelegate: MenuNavigationDelegate?, menuManagerDelegate: MenuManagerDelegate?
var conferenceId = "conferenceId-1"
var isDataLoading = false
#IBOutlet
var menuTableView: UITableView?
var menuItems = Array<MenuItemStruct>()
#IBAction
func closeMenuTapped() {
self.menuManagerDelegate?.closeMenu()
}
override func viewDidLoad() {
self.menuTableView?.register(UINib(nibName: "MenuTableViewCell", bundle: nil), forCellReuseIdentifier: "menuCell")
self.isDataLoading = true
MenuDataManager.getMenuInformation(conferenceId: self.conferenceId) {
menuItems, response, error in
self.menuItems = menuItems!
self.isDataLoading = false
DispatchQueue.main.async {
self.menuTableView?.reloadData()
}
}
self.menuTableView?.reloadData()
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:MenuTableViewCell = menuTableView!.dequeueReusableCell(withIdentifier: "menuCell", for: indexPath) as! MenuTableViewCell
if (!self.isDataLoading) {
cell.setUpCellWithData(menuItem: menuItems[indexPath.row])
}
return cell
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(!self.isDataLoading) {
return self.menuItems.count
}
return 0;
}
}
You need to set the delegate and datasource of your table view. You have included the UITableViewDataSource and UITableViewDelegate protocols, but have not assigned a delegate and datasource for your tableview in the code.
Try something like this in your viewDidLoad:
self.menuTableView.delegate = self
self.menuTableView.dataSource = self

Resources