Reputation: 477
I have a separate class from a UIViewController set up as my UITableView delegate and my UITableViewDataSource.
I attempt to initialize the UITableViewDelegate class and then assign it to the UITableView.
Here's what's odd...
The methods numberOfSectionsInTableView and tableView(:numberOfRowsInSection) are called five times, while tableView(_:cellForRowAtIndexPath) is never called.
I have verified both numberOfSectionsInTableView and tableView(:numberOfRowsInSection) return values of at least one.
If I move the UITableViewDataSource and UITableViewDelegate methods to the ViewController, the code works correctly.
What is causing this behavior?
class MessagesViewController: UIViewController, ManagedObjectContextSettable {
var managedObjectContext: NSManagedObjectContext!
@IBOutlet var messagesTableView: UITableView!
override func viewDidLoad() {
setupMessagesTableView()
}
private func setupMessagesTableView() {
let dataSource = MessagesTableViewDataSource(managedObjectContext: managedObjectContext, conversationList: fetchedObjects as! [Conversation])
// Assume fetchedObjects is an array fetched from CoreData store. I have removed the code that defines it for the purpose of this example.
self.messagesTableView.dataSource = dataSource
self.messagesTableView.delegate = dataSource
}
}
class MessagesTableViewDataSource: NSObject, UITableViewDataSource, UITableViewDelegate {
var managedObjectContext: NSManagedObjectContext!
var conversationList: [Conversation]
required init(managedObjectContext: NSManagedObjectContext, conversationList: [Conversation]) {
self.managedObjectContext = managedObjectContext
self.conversationList = conversationList
let conversation = Conversation()
self.conversationList.append(conversation)
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.conversationList.count }
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellID", forIndexPath: indexPath) as UITableViewCell!
let conversation: Conversation = self.conversationList[indexPath.row]
cell.textLabel!.text = conversation.name as? String
return cell
}
}
Upvotes: 1
Views: 520
Reputation: 2883
The problem is that your instance of MessagesTableViewDataSource gets deallocated. The delegate and dataSource properties on UITableView are weak. You declare your datasource (MessagesTableViewDataSource) as a local variable inside your function and thus nothing holds a strong reference to the instance of MessagesTableViewDataSource.
To fix this, define an instance variable for dataSource and assign it in viewDidLoad.
Example Code:
class MessagesViewController: UIViewController, ManagedObjectContextSettable {
let dataSource: MessagesTableViewDataSource?
var managedObjectContext: NSManagedObjectContext!
@IBOutlet var messagesTableView: UITableView!
override func viewDidLoad() {
setupMessagesTableView()
}
private func setupMessagesTableView() {
dataSource = MessagesTableViewDataSource(managedObjectContext: managedObjectContext, conversationList: fetchedObjects as! [Conversation])
// Assume fetchedObjects is an array fetched from CoreData store. I have removed the code that defines it for the purpose of this example.
self.messagesTableView?.dataSource = dataSource
self.messagesTableView?.delegate = dataSource
}
}
Upvotes: 1
Reputation: 9
I have the same problem and I believe this to be a bug in UIKit.
I created a simple list data source and a small view Controller to test this and I can confirm that cellForRowAtIndexPath
does not get called. 'numberOfRowsInSection' returns a value greater 0 and the tableView's frame is set correctly.
The same code works when put in the view controller. Maybe I'm missing something here, but I think this is a bug on Apple's side.
SimpleListDataSource.swift
import UIKit
class SimpleListDataSource : NSObject, UITableViewDataSource {
var items: [String]
var cellIdentifier: String
typealias CellConfiguration = (UITableViewCell, String) -> ()
var cellConfiguration: CellConfiguration
init(items: [String], cellIdentifier: String, cellConfiguration: CellConfiguration) {
self.items = items
self.cellIdentifier = cellIdentifier
self.cellConfiguration = cellConfiguration
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Number of rows: \(self.items.count)")
return self.items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print(__FUNCTION__)
let cell = tableView.dequeueReusableCellWithIdentifier(self.cellIdentifier, forIndexPath: indexPath)
self.cellConfiguration(cell, self.items[indexPath.row])
return cell
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let data = ["a", "b", "c"]
let dataSource = SimpleListDataSource(items: data, cellIdentifier: "cell") { (cell, string) -> () in
cell.textLabel?.text = string
}
self.tableView.dataSource = dataSource
self.tableView.reloadData()
}
}
Upvotes: 0
Reputation: 2281
Check the frame of your table view. If the height or width is 0, tableView:cellForRowAtIndexPath:
will not be called.
Upvotes: 0