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
}
Related
I am asked to do optimise my swift code especially when it comes to UI Delegate methods.
Scenario: I have UITableView in five UIViewControllers, in each view controller I am using UITableView delegate methods due to which my code is repeating. So how can I optimise this thing?
Below is the structure of code: You can see delegate method code is repeating
class FirstViewController: UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var userArray = [UserModel]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
// register cell according to model (Dynamic Cells)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell01", for: indexPath)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
class SecondViewController: UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var productArray = [ProductModel]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
// register cell according to model (Dynamic Cells)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell01", for: indexPath)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
i'm set tableview datasource but it doesn't recognize with his erorr
Type 'ViewController' does not conform to protocol 'UITableViewDataSource'
import UIKit
class ViewController: UIViewController ,UITableViewDataSource,UITableViewDelegate{
override func viewDidLoad() {
super.viewDidLoad()
var contact : [pedped]=[]
let asghar = pedped()
asghar.nmae="asghar "
asghar.lname="ghasemi"
asghar.image="asghar"
let akbar = pedped()
akbar.nmae="akbar "
akbar.lname="askj"
akbar.image="asgkajsjkashar"
contact.append(asghar)
contact.append(akbar)
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contact.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell=tableView.dequeueReusableCell(withIdentifier: "id", for: indexPath)as!custom
let contacts = contact[indexPath.row]
cell.lablename.text = contacts.nmae
cell.lastnamelable.text = contacts.lname
return cell
}
}
}
Although ViewController conforms to UITableViewDataSource, you are implementing the methods in wrong place. You have to remove them outside viewDidLoad.
Also, make sure that the table view data source/delegate is connected to the view controller itself, whether from the interface build or from the code:
// create an IBOutlet for the table view then:
tableView.dataSource = self
tableView.delegate = self
This probably won't work 100% but it at least shows you a better way to organize your code
import UIKit
class ViewController: UIViewController {
var contact = [pedped]() // Int
override func viewDidLoad() {
super.viewDidLoad()
let asghar = pedped()
asghar.nmae="asghar "
asghar.lname="ghasemi"
asghar.image="asghar"
let akbar = pedped()
akbar.nmae="akbar "
akbar.lname="askj"
akbar.image="asgkajsjkashar"
contact.append(asghar)
contact.append(akbar)
}
}
extension ViewController: UITableViewDataSource
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contact.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell=tableView.dequeueReusableCell(withIdentifier: "id", for: indexPath)as!custom
let contacts = contact[indexPath.row]
cell.lablename.text = contacts.nmae
cell.lastnamelable.text = contacts.lname
return cell
}
}
extension ViewController: UITableViewDelegate {
// Put any UITableViewDelegate functions here
}
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
}
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
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
}