TableView external dataSource and Delegate not loading - ios

I have a tableview that is added programatically below that I want to hook up the delegate and dataSource to an external class. The code looks right however the tableview gets added to the view without getting the cell layout from the external class.
let tableView: UITableView = {
let dataService = ActivityDataService()
let tb = UITableView()
tb.tableHeaderView = nil
tb.tableFooterView = nil
tb.rowHeight = 50
tb.estimatedRowHeight = 50
tb.dataSource = dataService
tb.delegate = dataService
tb.register(ProfileActivitySubCell.self, forCellReuseIdentifier: "tableCell")
return tb
}()
Here is the activity service class:
class ActivityDataService: NSObject, UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath) as! ProfileActivitySubCell
return cell
}
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
return 0
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
}
Thanks

When using a UITableView thats not in a storyboard or similar you need to register the cell with an identifier.
Depending on your UITableViewCell (if its a subclass and/or if you are using nibs or not)
You could use one of these methods:
open func register(_ nib: UINib?, forCellReuseIdentifier identifier: String)
open func register(_ cellClass: Swift.AnyClass?, forCellReuseIdentifier identifier: String)
Which is methods of UITableView
In your case probably something like this:
tb.register(ProfileActivitySubCell.classForCoder(), forCellReuseIdentifier: "tableCell")

1) Refactor the table view data source methods to a separate class
class IceCreamListDataSource: NSObject, UITableViewDataSource
{
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 5
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("IceCreamListCell", forIndexPath: indexPath)
return cell
}
}
2) In your controller class do this-:
class IceCreamListViewController: UITableViewController
{
let dataSource = IceCreamListDataSource()
// MARK: - View lifecycle
override func viewDidLoad()
{
super.viewDidLoad()
tableView.dataSource = dataSource
}
}

I managed to solve the issue. As the tableView was inside a collection view I had to use a storyboard object outlet. Finally inside the collection view cell I had to set the delegate and dataSource to the newly created object.
cell.tableView.dataSource = dataService
cell.tableView.delegate = dataService

Related

swift: tableview does not work after reloadData

i have a tableview in a viewcontroller and because i need to reuse most of the code for another table i created an extra class:
class StatisticsViewDelegate: NSObject, UITableViewDelegate, UITableViewDataSource {
var defaultList:[String]
var infolist:[String] = []
var tableView:UITableView
var controller:UIViewController?
init(defaultList:[String], view:UITableView, controller:UIViewController?) {
self.defaultList = defaultList
self.controller = controller
tableView = view
super.init()
tableView.delegate = self
tableView.dataSource = self
loadTable()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return infolist.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "infocell", for: indexPath) as! TableViewCell
// [fill cell]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// [...]
}
func loadTable() {
DispatchQueue.global(qos: .userInitiated).async {
//[...]
// in this case:
self.infolist = self.defaultList
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
and in my UITViewController in the viewDidLoad():
delegate = StatisticsViewDelegate(defaultList: defaultList, view: tableView, controller:self)
delegate is a member of the ViewController
now when i run it, the function func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) never gets called. The func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) gets called however(before and after the reload) and returns the correct number(in my case 4). The TableView isn't visible at all. Where is my error?
Maybe you can use the subclassing strategy to resolve your problem. There are many reference passed to your class and if you forgot to clean that up you will be have memory leaks in your hand. So I'll suggest the simple example as below. You can modify as you like and let me know if that was what you are after. If not please pardon me.
//This will be parent class that will handle all table methods, so you need to write only once the delegates and stuffs
class MyCommonTableController: UITableViewController {
var infoList = [String]()
// MARK: - TableView Delegate and Datsource Impl
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return infoList.count
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 55.0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = infoList[indexPath.row]
return cell
}
}
The first class that is directly subclassing the from above MyCommonTableController
//In here we just have to know the data and set the infoList from parent
class TheTableViewController: MyCommonTableController {
let defaultList = ["Data1","Data2","Data3"] //....etc
override func viewDidLoad() {
super.viewDidLoad()
//this is were I will set those
infoList = defaultList
//reload the table
tableView.reloadData()
}
}
The second class that is directly subclassing the from above MyCommonTableController. Same process goes here
class TheSecondTableViewController: MyCommonTableController {
let defaultList = ["List1","List2","List3"] //....etc
override func viewDidLoad() {
super.viewDidLoad()
//this is were I will set those
infoList = defaultList
//reload the table
tableView.reloadData()
}
}
And now you are not repeating and table methods. You can also get the reference of table and use in your norma table view
#IBOutlet weak var theTable: UITableView!
let defaultList = ["List1","List2","List3"] //....etc
let commonTable = MyCommonTableController()
// MARK: - LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
commonTable.infoList = defaultList
commonTable.tableView = theTable
}

Getting an input onclick in UITableView

I'm trying to get an input when the user taps on a cell of a UITableView. That's my code:
import UIKit
class TableView: UIViewController, UITableViewDataSource {
public var data = Array<Array<String>>()
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier")!
cell.textLabel?.text = data[indexPath.row][0]
cell.detailTextLabel?.text = data[indexPath.row][1]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("TEST")
}
}
And in ViewController I have:
...
let tvClass = TableView()
override func viewDidLoad() {
super.viewDidLoad()
tvClass.data = [["title", "name"]]
self.tableView.dataSource = tv
self.tableView.alwaysBounceVertical = false
}
...
Why am I not getting "TEST" when I click on a cell?
didSelectRowAt is a method inside UITableViewDelegate so , set table delegate like this in viewDidLoad
self.tableView.delegate = self;
and change class declaration like this
class TableView: UIViewController, UITableViewDataSource , UITableViewDelegate {
The Only missing thing is
TableViewDelegate.
As delegate tells iOS sdk to know that action is being performed.
self.tableView.delegate = self

Xcode 9 Swift 4 UITableView unrecognized selector sent to instance

I just moved from obj-c to swift and i am having problem with the UITableView inside a UIViewController
I have set the delegate and data source from the Interface Builder from the TableView to the View Controller, but it doesn't work fine
here is the screenshot
and here is my view controller
class SideMenuViewController: UIViewController {
#IBOutlet var tableView: UITableView!
var cellRow = ["Item 1","Item 2","Item 3"]
// MARK: - TableView Delegates
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellRow.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let object = cellRow[indexPath.row]
cell.textLabel!.text = object.description
return cell
}
Here is the error
reason: '-[MyApp.SideMenuViewController tableView:numberOfRowsInSection:]: unrecognized selector sent to instance
However, if i set the delegate and data source manually by using code inside the view controller, it will work. Is it a bug??
Thanks
You must add UITableViewDelegate, UITableViewDataSource. Delegate class have to tell compiler that we are adopting the protocol and then implement the delegate method(s).
class SideMenuViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// ...
}
Another good looking way is to implement protocols and delegates is by using extension.
class SideMenuViewController: UIViewController {
// ..
}
extension SideMenuViewController : UITableViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let object = cellRow[indexPath.row]
cell.textLabel!.text = object.description
return cell
}
}
extension SideMenuViewController : UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellRow.count
}
}
Swift 4
You can just add delegate like this
Step 1 : class ViewController: VVBaseViewController, UITableViewDelegate, UITableViewDataSource
Step 2 Implement Delegate And DataSource Method
// MARK: - UITableViewDelegate, UITableViewDataSource
func numberOfSections(in tableView: UITableView) -> Int {
return 20 // Here is 20 section in tableview and 1 row each section.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1 // Only 1 row in each section
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Normal Cell (UITableViewCell)
// let cell:UITableViewCell = (tblProperty!.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell?)!
// Custom Cell
let cell:CellSubService = tblSubServices.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! CellSubService
cell.lblServiceName.text = "Test Service"
cell.lblRate.text = "$5 / Sq.Ft"
return cell
}

variable cell is never mutated

When I take var type variable then Xcode show warning
variable is never mutated
If I take let type variable then don't show any result!
import UIKit
class ViewController: UIViewController,UITableViewDataSource{
let people = [
("Pankaj","Dhaka"),
("Asish","Madaripur"),
("Anup","Narail")
]
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "People"
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return people.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let (personName , personLocation) = people[indexPath.row]
cell.textLabel?.text = personName
cell.textLabel?.text = personLocation
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}
You cannot use a table view cell initialized with the default initializer UITableViewCell()
Reuse the cell, add an identifier (e.g. PeopleCell) in Interface Builder.
let cell = tableView.dequeueReusableCell(withIdentifier: "PeopleCell", for: indexPath)
And make sure that datasource and delegate of the table view are connected in Interface Builder, too.

UITableView delegate using extensions swift

This is a fairly simple question I think. I've separated my UITableView delegate / data sources into their own extensions
//MARK: - UITableView Data Source/Delegate
extension TweetsViewController: UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! TweetCell
return cell
}
}
However in the view controller itself I need to set the tblView delegate
class TweetsViewController : UIViewController {
#IBOutlet weak var tblView: UITableView!
var fetchedResultsController : NSFetchedResultsController!
//MARK: View Management
override func viewDidLoad() {
super.viewDidLoad()
tblView.dataSource = self
}
}
However, since the view controller is nor conforming to the protocols but having the extensions handle them, then how do I explicitly set the datasource and delegate for the tableView? Thanks!
You can divide in a extension, as you can check in the apple documentation section about Extensions handling Protocols.
Here I have implement a minimum code doing what you ask, check it out.
import UIKit
class TableViewViewController: UIViewController {
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
}
}
extension TableViewViewController: UITableViewDelegate,UITableViewDataSource {
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)
cell.textLabel!.text = "it works"
return cell
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
}
In Swift 3 and above the table view datasource and delegate methods changed.
import UIKit
class HomeViewController: UIViewController {
#IBOutlet var tblPropertyList: UITableView!
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tblPropertyList.delegate = self
tblPropertyList.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
// MARK: - Table View DataSource
extension HomeViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath as IndexPath)
cell.textLabel!.text = "\(indexPath.row) - Its working"
return cell
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
}
// MARK: - Table View Delegate
extension HomeViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexPath = tableView.indexPathForSelectedRow
let currentCell = tableView.cellForRow(at: indexPath!)!
print(currentCell.textLabel!.text!)
}
}
the view controller is nor conforming to the protocols but having the extensions handle them
This is incorrect. The extension makes the view controller conformant to the protocols, and the data source and delegate can be set as usual, e.g.: self.tableView.delegate = self
Now in Swift 5.1 you don't need to inherit UITableViewDelegate and UITableViewDataSource
extension HomeViewController {
// MARK: - Table View DataSource
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath as IndexPath)
cell.textLabel!.text = "\(indexPath.row) - Its working"
return cell
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
}
// MARK: - Table View Delegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexPath = tableView.indexPathForSelectedRow
let currentCell = tableView.cellForRow(at: indexPath!)!
print(currentCell.textLabel!.text!)
}
}

Resources