I'm using Xcode 7.0, Swift 2
I'm basically trying to create a custom class that will build a UITable, then in the ViewController I make a new object of the table and load it into self.view;
The problem I'm having is that the function func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell isn't being called at all from within the custom class. I've been looking for a solution for 3 days now and I've tried rebuilding the App and code several times with no luck.
Please note, if I use the same code (that is everything required to build the table; excluding init functions, etc) in the ViewController.swift file, it works fine.
I know the problem is with the cellForRowAtIndexPath function because it will not print out the statement I set in that block of code when it runs. All other functions are called, but for some reason this isn't being called. Not sure if I overlooked something here. Any help would be appreciated. Thanks in advance.
class sideTest: NSObject, UITableViewDelegate, UITableViewDataSource {
let tesTable: UITableView = UITableView()
var items: [String]?
var mView: UIView = UIView()
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("The number of rows is: \(self.items!.count)")
return self.items!.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("\nLets create some cells.")
let sCell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell!
sCell.textLabel?.text = self.items![indexPath.row]
sCell.textLabel?.textColor = UIColor.darkTextColor()
return sCell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You selected cell #\(indexPath.row)!")
}
func tblSetup() {
self.tesTable.frame = CGRectMake(0, 0, 320, mView.bounds.height)
self.tesTable.delegate = self
self.tesTable.dataSource = self
self.tesTable.backgroundColor = UIColor.cyanColor()
// load cells
self.tesTable.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
self.tesTable.reloadData()
print("Currenlty in tblSetup.\nCurrent rows is: \(self.items!.count)")
}
//Init
override init() {
super.init()
self.items = nil
self.tblSetup()
}
init(sourceView: UIView , itemListAsArrayString: [String]) {
super.init()
self.items = itemListAsArrayString
self.mView = sourceView
self.tblSetup()
}
}
Here is the code from ViewController.swift; Please do note that the table gets built, but the cells do not populate, even if I manually enter cell info by doing: sCell.textLabel?.text = "test cell"
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let myTable: sideTest = sideTest(sourceView: self.view, itemListAsArrayString: ["Cell 1", "Cell 2", "Cell 3"])
self.view.addSubview(myTable.tesTable)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Again, any help is greatly appreciated. Thanks.
Your view controller don't have a strong reference to your sideTest var.
Once your view did load finished,your sideTest is nil.Although you have a tableview(by add subview), but you no longer have a data source.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {}
is called after view did load. That cause the problem.
change your view controller to:
var tb :sideTest?
override func viewDidLoad() {
super.viewDidLoad()
let myTable: sideTest = sideTest(sourceView: self.view, itemListAsArrayString: ["Cell 1", "Cell 2", "Cell 3"])
print(myTable.tesTable.frame)
tb=myTable
self.view.addSubview(myTable.tesTable)
}
change your cellforrowatindexpath to:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("create cells")
var cell :UITableViewCell?
if let sCell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("cell"){
cell=sCell
}else{
cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
}
cell!.textLabel?.text = self.items![indexPath.row]
cell!.textLabel?.textColor = UIColor.darkTextColor()
return cell!
}
this will fix most of the problems.
Your code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let myTable: sideTest = sideTest(sourceView: self.view, itemListAsArrayString: ["Cell 1", "Cell 2", "Cell 3"])
self.view.addSubview(myTable.tesTable)
}
I would think that the myTable variable goes out of scope and is released when viewDidLoad finishes, so there is no data source or delegate after that. Did you verify that the self.view.addSubview(myTable.tesTable) retains it? Try moving declaration of myTable outside of the function level (to property level) or add a diagnostic print to deinit..
Related
I know this particular question has been asked and answered previously in SO but cross checked those answers and still not able to fix this issue. Can be a silly mistake but unable to nail it.
Cross checked :
Cell Identifierid
datasource and delegate added through Interface builder
Code :
var sensorFields = [Sensor]()
override func viewDidLoad() {
super.viewDidLoad()
readParseDataFromCSV(file: "csvFile")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(self.sensorFields.count)// has count 120
return self.sensorFields.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SensorTableCell") as! SensorTableViewCell
cell.lblSensorName.text = sensorFields[indexPath.row].labelName
cell.lblSensorValue.text = sensorFields[indexPath.row].labelValue
print(sensorFields[indexPath.row].labelName) // doesn't seem to enter to this code block
print(sensorFields[indexPath.row].labelValue)
return cell
}
func readParseDataFromCSV(file:String){
let filepath = Bundle.main.path(forResource: file, ofType: "csv")!
do {
let csv = try CSV(contentsOfURL: filepath)
let rows = csv.rows
for row in rows{
let sensorlblName = row["data column 1"]
let sensorlblValue = row["data column 2"]
let sensor = Sensor(labelName: sensorlblName!, labelValue: sensorlblValue!)
sensorFields.append(sensor)
}
} catch {
print(error.localizedDescription)
}
}
class SensorTableViewCell: UITableViewCell {
#IBOutlet weak var lblSensorName: UILabel!
#IBOutlet weak var lblSensorValue: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Is there anything missed out , using other tableviews in the app already which is working perfectly apart from this one.
Using Xcode 8.1 and Swift 3.0
Edit :
Added reloadData()
Seems like
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}
is not firing too.
Please help.
Thanks in advance.
Just appending data to your sensorFields array has no impact on the cells displayed in your tableView.
You need to call reloadData on the tableView so it knows the data did change and repopulates the cells with the new data.
for row in rows {
let sensorlblName = row["data column 1"]
let sensorlblValue = row["data column 2"]
let sensor = Sensor(labelName: sensorlblName!, labelValue: sensorlblValue!)
sensorFields.append(sensor)
}
tableView.reloadData()
I would also prefer
// newer dequeue method guarantees a cell is returned and resized properly, assuming identifier is registered
// Swift 2
func dequeueReusableCellWithIdentifier(identifier: String, forIndexPath indexPath: NSIndexPath) -> UITableViewCell
// Swift 3
func dequeueReusableCell(withIdentifier identifier: String, for indexPath: IndexPath) -> UITableViewCell
to
// Used by the delegate to acquire an already allocated cell, in lieu of allocating a new one.
// Swift 2
func dequeueReusableCellWithIdentifier(identifier: String) -> UITableViewCell?
// Swift 3
func dequeueReusableCell(withIdentifier identifier: String) -> UITableViewCell?
because dequeueReusableCellWithIdentifier(identifier: String) does not guarantee returning a UITableViewCell when the cell with identifier was not yet allocated. ("[...] acquire an already allocated cell, in lieu of allocating a new one.")
And make sure your IBOutlets are connected! (lblSensorName and lblSensorValue)
check this
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell = tableView.dequeueReusableCellWithIdentifier("SensorTableCell") as UITableViewCell!
if !cell {
cell = UITableViewCell(style:.Default, reuseIdentifier: "SensorTableCell")
}
}
I have a main menu in my app with 15 item and each item has a sub of 20 items which i also added them as images array (15 image array , some thing like a restaurant menu ) so when ever the user clicks on 1 of the main menu items the app will take him to the sub menu , i have created the main menu table view the issue is with the sub menus do i have to create 15 table view for each sub menu !!??
is there is any way to create 1 table view for the sub menus and change its data according to user click
note : i don't want to use the sections in my table view
any ideas will be much appreciated
Need two viewControllers and a navigationController. One for main menu and other for sub menu. Let them be mainMenuViewController and subMenuViewController. Each controllers contains a tableView.
Create an menuArray containing 15 submenu data.Each submenu is an array.
In didSelectRowAtIndexPath of mainMenuViewController, if user selects a row in the tableView, then pass that data in the menuArray corresponding to the selected row.
For example, if user selects third row,
then pass menuArray[3] to subMenuViewController. Here indexPath.row = 3.
Sample Project Code:
MenuViewController.swift:
import UIKit
class MenuViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var menuTableView: UITableView!
var imagesArray: NSArray = []
var menuArray: NSArray = []
var subMenuDataArray: NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
menuTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
imagesArray = ["soups.jpg","salads.jpg","starters.jpg","pizzas.jpg","burgers.jpeg"]
menuArray = ["Soups","Salads","Starters","Pizzas","Burgers"]
subMenuDataArray = [["Cream of broccoli","Cream of celery","Cream of tomato","Etrog","Gazpacho"],
["Tuna salad","Urnebes","Waldorf salad"],
["Kakori Kebabs","Stir Fried Chilli Chicken"," Microwave Paneer Tikkas","Aloo and Dal ki Tikki","Cheese Balls","Bhuna Masala Chicken Wings"],
["Cheese Pizzas","Chicken Pizzas","Masala Pizzas","Double Cheese Pizzas","Herbal Pizzas"],
["Luger Burger","Le Pigeon Burger","The Company Burger","Dyer’s Deep-Fried Burger","The Lola Burger","Cheeseburger","Raw Steak Tartare Burger","Buckhorn Burger"]]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return imagesArray.count
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat{
return 70
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let menuTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
let cellImageView: UIImageView = UIImageView.init()
cellImageView.frame = CGRectMake(10, 10, 50, 50)
cellImageView.image = UIImage(named: imagesArray.objectAtIndex(indexPath.row) as! String)
menuTableViewCell.contentView.addSubview(cellImageView)
let menuLabel: UILabel = UILabel.init(frame: CGRectMake(70, 10, 200, 25))
menuLabel.text = menuArray.objectAtIndex(indexPath.row) as? String
menuTableViewCell.contentView.addSubview(menuLabel)
return menuTableViewCell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
let subMenuViewController: SubMenuViewController = storyboard!.instantiateViewControllerWithIdentifier("SubMenuViewControllerID") as! SubMenuViewController
subMenuViewController.currentSubMenuArray = subMenuDataArray.objectAtIndex(indexPath.row) as! NSArray
navigationController?.pushViewController(subMenuViewController, animated: true)
}
}
SubMenuViewController.swift:
import UIKit
class SubMenuViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var subMenuTableView: UITableView!
var currentSubMenuArray: NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
subMenuTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "subCell")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return currentSubMenuArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let subMenuTableViewCell = tableView.dequeueReusableCellWithIdentifier("subCell", forIndexPath: indexPath)
let subMenuLabel: UILabel = UILabel.init(frame: CGRectMake(10, 10, 250, 25))
subMenuLabel.text = currentSubMenuArray.objectAtIndex(indexPath.row) as? String
subMenuTableViewCell.contentView.addSubview(subMenuLabel)
return subMenuTableViewCell
}
}
Storyboard:
Output:
To test the sample project, use the following link of my GitHub account:
https://github.com/k-sathireddy/MenuTableViewSample
Declare one array for displaying and change the content of the array according to the selection and screen state like for menu, submenu etc. and reload the table to display the data in the main array. And if you want to display different kind of cell for different selection you can achieve it by taking an enum for whats the current screen state like i said and return required cell initialized in cellForRowAtIndexPath. Its all about how much you can think and implement the logic. Comment below if you need real technical solution with codes you already have used.
I have written this code:
//Basic methods
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.DevicesTableView.registerClass(UITableViewCell.self,forCellReuseIdentifier: "cell")
self.DevicesTableView.dataSource = self
}
var array = ["one","two"]
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return array.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell: UITableViewCell! = self.DevicesTableView
.dequeueReusableCellWithIdentifier("cell") as UITableViewCell!
cell.textLabel!.text = self.array[indexPath.row]
return cell
}
So the code works pretty well and shows me the tableview filled with:
Row1 "One"
Row2 "Two"
But how can I fill the TableView not at the beginning of my program but anytime else? I tried to call the methods below in an other function that viewDidLoad but it doest't work... why?
self.DevicesTableView.registerClass(UITableViewCell.self,forCellReuseIdentifier: "cell")
self.DevicesTableView.dataSource = self
EDIT:
I would like to display an array which isn't initialized at the beginning of my program. It contains some BLE devices after the user searched for them.
Its very simple
Update your data in your array
Then reload your tableview by using
your_tableview.reloadData()
Ex:
var array = ["one","two","Three","Four"] // your array will take no.of.row in section
DevicesTableView.reloadData()
Table views have a reloadData method which does what you want.
I've searched around for an answer to this one but haven't had any success. I am essentially following a tutorial to create a simple todo app, many other's are commenting with the same error as below. The author doesn't have a solution yet. Any help would be appreciated.
I'm getting the error: 'UITableViewCell?' does not have a member named 'textLabel'
Here's my code so far:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// tells iphone what to put in each cell
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
// Tells iphone how many cells ther are
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
cell.textLabel?.text = "table cell content"
return cell!
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I am following the same tutorial, so I can feel your pain! :) In the tutorial the fellow has you delete and recreate the view controller, only then he forgets to mention that you need to name your view controller again. Any way, to save some aggravation, just create a new project, drop a Table View into the view controller, right click on the Table View, and link dataSource, delegate, and view to the View Controller.
Next, here is the code that works for me as of XCode 6.1. God knows what they are going to change next. But in short, you don't need the '?' after textLabel.
import UIKit
class ViewController: UIViewController, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var items = ["test 1", "test 2", "test 3", "test 4"]
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
cell.textLabel.text = self.items[indexPath.row]
return cell
}
}
Hope that helps.
I had to fight with the same / a similar issue today. It happens because you are trying to use custom cells in a standard table view controller. You need to tell the controller in the function that the cell with its custom name should be used as the (= instead of the) TableViewCell. Then Xcode will know where to look for the names.
So right after the closing braces for the indexPath you type:
as! TableViewCell
Try this:
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text = "table cell content"
return cell!
This should do the trick
cell?.textLabel?.text = array[indexPath.row]
I'm assuming you are using 6.1 as this is not an issue in 6.0.1, but they have changed things once again in the latest release.
Hope this works for you
EDIT Answer below in this post
I'm trying to set up a UITableView controller in storyboard, with a separate datasource, and I've hit a wall. The data source doesn't seem to respond to changes or push it's 'updates' to the table view. I've tried implementing the data source in the MainMenuTableViewController which worked fine.
This is my MainMenuTableViewController
override func viewDidLoad() {
super.viewDidLoad()
sharedLightsManager.delegate = self
sharedLightsManager.loadNetworkContext()
dataSource = MainMenuTableViewDataSource(sharedLightsManager: sharedLightsManager)
tableView.dataSource = dataSource
tableView.delegate = dataSource
title = "test"
}
//This method fires each time a change happens
func updateLights(){
lights = sharedLightsManager.localNetworkContext.allLightsCollection.lights
tableView.reloadData()
}
MainMenuDataSource:
class MainMenuTableViewDataSource: NSObject, UITableViewDataSource, UITableViewDelegate
{
let reuseIdentifier = "tableViewCell"
var sharedLightsManager: SharedLightsManager?
var lights = []
init(sharedLightsManager: SharedLightsManager)
{
self.sharedLightsManager = sharedLightsManager
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lights.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("tableViewCell", forIndexPath: indexPath) as UITableViewCell
var lights = sharedLightsManager!.localNetworkContext.allLightsCollection.lights
var light = LFXLight()
if lights.count == 0 {
println("Lights array still loading...")
} else {
light = lights[indexPath.row] as LFXLight
}
return cell
}
}
and here is my outlets:
I've just figured it out. A bit embarrassing. It was due to the lights array not having any objects in it, so obv. lights.count would return 0, therefore no rows...
The data source will not push updates unless the UITableView is told to reloadData. If you change the numberOfRows value, it will not update unless the tableView is notified through methods like insertRowAtIndexPath, reloadData, deleteRowAtIndexPath etc.