Xcode 9 Swift 4 UITableView unrecognized selector sent to instance - ios

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
}

Related

Trying to set the row height in a tableView for the cells

I'm updating an app of mine, and I have used this method in other areas of the app and it works, but for some reason, it's not working on a tableView.
The tableView is inside of a ViewController (CurrencyViewController)
#IBOutlet weak var tableView: UITableView!
tableView.dataSource = self
extension CurrencyViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 200.0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return currencies.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "currencyCell")! as! CurrencyTableViewCell
cell.name.text = currencies[indexPath.row].currencyName
cell.name.textColor = Styles.whiteColor()
cell.symbol.text = currencies[indexPath.row].currencyCode
cell.symbol.textColor = Styles.whiteColor()
cell.backgroundColor = Styles.mainColor();
return cell
}
}
The tableView itself works, it's just not updating the height of the row.
Did something change with the update of Swift?
You don't seem to have conformed to the UITableViewDelegate ?
extension CurrencyViewController: UITableViewDataSource, UITableViewDelegate
If dataSource (and delegate) is connected in IB delete
tableView.dataSource = self
You must also adopt UITableViewDelegate in the extension, connecting the delegate is not sufficient
extension CurrencyViewController: UITableViewDataSource, UITableViewDelegate {

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

func tableview didSelectRowAtIndexPath is not called [duplicate]

This question already has an answer here:
didSelectRowAtIndexPath not working, Swift 3
(1 answer)
Closed 5 years ago.
I'm trying to call viewModel.userDidSelectItem(identifier: "DetailMainViewController") function when the user clicks on a cell but I'm not able, first print(indexPath.row) is nil and second I don't know if its correct the call.
import Foundation
import UIKit
class MainViewController: UIViewController, UITableViewDataSource {
#IBOutlet var tableview:UITableView!
var viewModel: MainViewModel = MainViewModel()
override func viewDidLoad() {
super.viewDidLoad()
viewModel.loadUsers {
self.tableview?.reloadData()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.fakeUsers?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "fakeUserObjectCell") as! MainTableViewCell
cell.fakeUserObject = viewModel.fakeUsers?[(indexPath as NSIndexPath).row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print(indexPath.row)
viewModel.userDidSelectItem(identifier: "DetailMainViewController")
}
}
when you are using the tableview you need to set the UITableViewDataSource and UITableViewDelegate to the MainViewController.
And override the below function
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

TableView external dataSource and Delegate not loading

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

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