I have a tableview that should have its delegate and datasource properties set from InsertIntoTableViewDelegate and InsertIntoTableViewDatasource Classes respectively. However, my InsertIntoTableViewDatasource class always returns null.
Here is the code:
class InsertIntoTableViewDatasource: NSObject,UITableViewDataSource {
var data:NSMutableArray
init(With data: NSMutableArray){
self.data = data
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("rowCell", forIndexPath: indexPath)
cell.textLabel?.text = data[indexPath.row] as? String
return cell
}
}
Here is the how the tableview is being set:
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Testing"
tableView.dataSource = InsertIntoTableViewDatasource(With: data)
tableView.delegate = InsertIntoTableViewDelegate()
}
Why is this the case?
UITableView does not retain its datasource, so your datasource is getting deallocated as soon as your viewDidLoad function ends.
You need to hold a reference to your datasource as a property in your view controller. Same thing with the delegate.
Related
I have a view controller, in the viewdidload method I call a function to make some calls to retrieve data online.
After I retrieve the data I append them into an array and reload the table view
at first, everything looks great however, when I scroll down I get to see what you see down below.
extension CastTableViewController : UITableViewDelegate, UITableViewDataSource {
func conigure_cast_table () {
castTable.translatesAutoresizingMaskIntoConstraints = false
castTable.delegate = self
castTable.dataSource = self
castTable.register(ActorCell.self, forCellReuseIdentifier: ActorCell.cellid)
view.addSubview(castTable)
castTable.frame = view.bounds
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cast.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = castTable.dequeueReusableCell(withIdentifier: ActorCell.cellid, for: indexPath) as! ActorCell
let name = cast[indexPath.row].name
let character_name = cast[indexPath.row].character
let pic = cast[indexPath.row].picture_path
cell.configure(name: name, charName: character_name, pic_url: pic)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
}
i fixed this by using prepareforreuse
// in the custom cell class
override func prepareForReuse() {
super.prepareForReuse()
for subview in stack.arrangedSubviews {
stack.removeArrangedSubview(subview)
}
actorPic.image = nil
realName.banner_title.text = nil
realName.info.text = nil
character.banner_title.text = nil
character.info.text = nil
}
Problem:
tableView Controller is not showing categoryArray items in table.
Diagnose:
- 1st tableView numberOfSections method is triggered and returns 1 (print(categoryArray.count)
- 2nd tableView cellForRowAt method never starts (print statement does not work)
- Table View Controller does not need delegate. However, I tried to add tableView.delegate = self
- x-code restart did not help
Question:
Why the 2nd tableView method does not work and how to fix this?
import UIKit
import CoreData
class CategoryViewController: UITableViewController {
var categoryArray = [Category]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
override func viewDidLoad() {
super.viewDidLoad()
loadCategories()
}
// MARK: - TableView DataSource Methods
override func numberOfSections(in tableView: UITableView) -> Int {
return categoryArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("this should print")
let cell = tableView.dequeueReusableCell(withIdentifier: "CategoryCell", for: indexPath) as! CategoryCell
let category = categoryArray[indexPath.row]
cell.categoryNameTextField.text = category.name
return cell
}
Instead of numberOfSections implement numberOfRows
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return categoryArray.count
}
At the end of viewDidLoad reload the table view
override func viewDidLoad() {
super.viewDidLoad()
loadCategories()
tableView.reloadData()
}
If loadCategories() contains something asynchronous reload the table view when the data are loaded.
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
I've added TableView to my controller and created custom UITableViewCell. Later, I've tried to run this code:
func tableView(educationTableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> educationCell {
let cell:educationCell = educationTableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! educationCell
print("TableView runs")
return cell
}
but I do not get my print in logs TableView runs. I do not understand what happens and why it doesn't run. Who knows, why this problem appears?
I read other answers in SO, but noone helps =/
need the return value of numberOfRowsInSection greater than 0
Just make sure you have implemented proper DataSource and Delegate for controller, number of rows is greater then 0
import UIKit
class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var items: [String] = ["We", "Heart", "Swift"]
#IBOutlet var tableView: UITableView! //hook this to your storyboard tableview
// MARK: - Controller life cycle stack
override func viewDidLoad() {
super.viewDidLoad()
//confirm table's delegate and data source programmatically
tableView.delegate = self;
tableView.dataSource = self;
}
//table view delegate and data source methods
func tableView(tableView:UITableView, numberOfRowsInSection section:Int) -> Int
{
//number of rows must be greater then 0 to get called "CellForRowAtIndex"
return self.items.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! CraditCardCell
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
print("You selected cell");
}
}
One reason could be because you modified the return type to educationCell, instdead of the standard UITableViewCell. Try changing that.
Still not solved!
I have been stuck with this problem way too long, so i hope you can help.
I Have two UITableViews side-by-side in one view in Storyboard. Each in a ContainerView to control their positions.
The idea is, if you touch a row in the first UITableView. The data from that row should be added to the second UITableView.
In TableOne i'm calling the function addDataToTableView() in didSelectRowAtIndexPath.
In Tabletwo addDataToTableView() get's the touched element and add it to the testData2 array. This works fine. The print() function spits out the right element.
But then on self.tableTwo.reloadData() the application crash with this error message:
fatal error: unexpectedly found nil while unwrapping an Optional value
I’m not entirely sure what causes the Error, but i think it’s either that i don’t get the right instance of the class, created by the storyboard or it’s something with optimals.
i tried putting in some ??? and som !!! and i have tried wrapping .reloaddata() ind another thread. but it didn’t help.
This is the entire code.
Hope you can help :)
TableOne
class TableOne: UITableViewController {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var tableTwo = TableTwo()
var testData = ["test1","test2", "test3", "test4", "test5", "test6"]
override func viewDidLoad() {
super.viewDidLoad()
tableTwo = mainStoryboard.instantiateViewControllerWithIdentifier("tableTwoId") as! TableTwo
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = testData[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableTwo.addDataToTableView(testData[indexPath.row])
}
}
TableTwo
class TableTwo: UITableViewController {
#IBOutlet var tableTwo: UITableView!
var testData2 = ["Test"]
func addDataToTableView(data: AnyObject) {
testData2.append(data as! String)
print("This works fine \(testData2[testData2.count-1] )")
self.tableTwo.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testData2.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = testData2[indexPath.row]
return cell
}
}
If you want to send data from One TableViewController either use prepareForSegue or didSelectRowAtIndexPath.
This line let cell = UITableViewCell() you only declare an instance of a UITableViewCell by you never called the method which makes that cell reusable in the queue.
class TableOne: UITableViewController
{
var testData = [String]()
override func viewDidLoad()
{
super.viewDidLoad()
testData = ["test1","test2", "test3", "test4", "test5", "test6"]
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return testData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath)
cell.textLabel?.text = testData[indexPath.row]
return cell
}
"CellIdentifier" is the name of the cell identifier from the tableView cell prototype into the storyboard
To send Data the TableView lets say we are using prepareForSegue and you must know that When the user selected one cell only that cell information will be sent to your next ViewController.
if segue.identifier == "NAME_OF_THE_SEGUE_IDENTIFIER"
{
let destination = segue.destinationViewController as("nameOftheTableView")
let indexPath = mytableview.indexPathForSelectedRow!
var dataToTransfer = testData[indexPath.row]
// let's say you have a string variable into your next ViewContrller called receiver
destination.receiver = dataToTransfer //<-- from the selectRow we assign that value into our next Controller.
}
So Build up on that to fix your code :)