I am coming to you for a little issue I have, that might actually not be that complicated but that i have struggled about for now about an hour...
I defined a TableViewCell in a TableViewController. This TableViewCell has three UIImage and two textLabels. I implemented the following methods :
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fruits.count
}
(which in my sense defines the number of rows)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "friendListCell")
cellOutlet.nameTextLabel.text = fruits[indexPath.row]
return cell!
}
On this second method, the "cellOutlet" is an instance of the class that defines my cell, in which I modify one of the textLabel's text.
"fruits" is a simple array of Strings that are supposed to be displayed in every cell of my app one by one.
Compilation goes fine, but when accessing the page in question, the app crashes saying
unexpectedly found nil while unwrapping an Optional value
My question is the following :
Would you have any idea of what is done wrong in term of accessing my textLabel's text ?
Thanks in advance !
If you're using a custom TableViewCell class you have to
Set the class of the cell in Interface Builder to the custom class
Cast the cell accordingly in cellForRow... (assuming the name is MyTableViewCell)
let cell = tableView.dequeueReusableCell(withIdentifier: "friendListCell" for:indexPath) as! MyTableViewCell
Try to call
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "friendListCell")
cell.nameTextLabel.text = fruits[indexPath.row]
return cell!
}
and also check "cell" should not be nil at the time of calling cell.nameTextLabel.text = fruits[indexPath.row]
Even if your custom UITableViewCell class (e.g. FruitsCell) has some labels, the standard UITableViewCell doesn't (apart from one, which is its 'label' property). In your code, the 'cell' property is of type UITableViewCell, which has no cellOutlet or nameTextLabel property to access.
If you set up your custom cell in IB, be sure to connect all your labels from IB with the code in your Swift-File, e.g. FruitsCell.swift, that contains the FruitsCell class.
Then cast the 'cell' in your ViewControllers delegate method as e.g. FruitCell instead of UITableViewCell. Only then you can access the labels of that class that you set up in IB.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// casting to your Class
let cell = tableView.dequeueReusableCell(withIdentifier: "friendListCell") as! FruitCell
// accessing that Class' properties
cell.nameTextLabel.text = fruits[indexPath.row]
// returning cell of type FruitCell
return cell!
}
Related
I need to add a label to a prototype cell but I am getting the error "Value of type 'UITableViewCell' has no member 'movieTitleLabel'.
import UIKit
class FirstTableView: UITableViewController {
let model = MovieModel()
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return model.MovieArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "movieCells", for: indexPath) as! TableViewCell
cell.movieTitleLabel?.text = model.movieArray[indexPath.row]
return cell
}
I have the FirstTableView set as the delegate and datasource, in the editor.
I created a new subclass named: TableViewCell, linked the label to that class. Now I am at at loss as to how to output the movieArray to that label.
You forgot to downcast cell to your UITableViewCell subclass so you have reference just to superclass UITableVoewCell and this superclass hasn’t any movieTitleLabel.
To fix this, add as! YourTableViewCellSubclass to the end of the line where you dequeue cell
let cell = tableView.dequeueReusableCell(withIdentifier: "movieCells", for: indexPath) as! YourTableViewCellSubclass
Look out into these points:
You have created an outlet of the label named as 'movieTitleLabel' in ViewController, but this label should be in TableCell class. Make correct reference of this label.
After the first point, now to access this label, follow this code:
Code:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "YourTableCellIdentifier", for: indexPath) as! YourTableCellClass
cell.movieTitleLabel.text = "Text" // Set your text here now
return cell
}
Your prototype cell should have specified an implementation class - this must be a subclass of UITableViewCell. If you've done this, downcast the result of dequeueReusableCell to this class.
You should also have connected the movieTitleLabel outlet in that class to the label in the storyboard. It looks from the above as if you've connected it to the UITableViewController instead (#IBOutlet weak var movieTitleLabel: UILabel!). You should remove this from the VC and only have it in the UITableViewCell class.
This is a part of my storyboard:
this is my running app:
This is my part of codes:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
if indexPath.row == 0 {
return super.tableView(tableView, cellForRowAt: indexPath)
} else {
tableView.register(SubTextFieldCell.self, forCellReuseIdentifier: "SubTextFieldCell")
let cell = tableView.dequeueReusableCell(withIdentifier: "SubTextFieldCell", for: indexPath) as! SubTextFieldCell
// cell.deleteButton.isEnabled = true
// cell.subTextfield.text = "OK"
print("indexPath.row: \(indexPath.row)")
return cell
}
...
I have already connected the button and the textfield in various places and I can guarantee that this part is not wrong, but when I click the Add button in the first row, I only get a cell without any content.
If I use code like this cell.deleteButton..., Xcode will report an error:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an
Optional value
Then I tried to use the viewWithTag method to see if show the content, but I still get the same error as before.
This is the first time I have encountered this kind of error. I have no error with similar code and methods in my other programs.
When you configure custom cells inside a storyboard file, you don't need to call register(_:forCellReuseIdentifier:) because the storyboard should have done that for you.
The reason deleteButton is nil is because by re-registering the cell class as you did, you overwrote what the storyboard registered for you. All cells created by dequeueing with that reuse identifier will have no connection to the storyboard and simply be empty.
Assuming all the #IBOutlets and reuse identifiers and things are set up (which you said you did), then simply dequeue the cell with the reuse identifier set up in storyboard.
Dequeue Cell Example:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
if indexPath.row == 0 {
return super.tableView(tableView, cellForRowAt: indexPath)
} else {
// Registering again is unnecessary, because the storyboard should have already done that.
// tableView.register(SubTextFieldCell.self, forCellReuseIdentifier: "SubTextFieldCell")
let cell = tableView.dequeueReusableCell(withIdentifier: "SubTextFieldCell") as! SubTextFieldCell
cell.deleteButton.isEnabled = true
cell.subTextfield.text = "OK"
return cell
}
} else {
...
}
}
Note:
Even in cases where you do need to register a class with a table view, you should only have to do this once. (For example, during viewDidLoad)
Even in those times, you should not call it every time you dequeue a cell. You're just making your app work harder.
Connecting views to cells in Storyboard
Set a subclass to the table view
Set a subclass to the first prototype cell
Set a reuse identifier to the prototype cell
Make sure subview (UIButton, etc.) is connected to property with #IBOutlet (subclass code shown below)
Example UITableViewController subclass:
class MyTableViewController: UITableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyFirstCell", for: indexPath) as! MyFirstTableViewCell
// Configure cell if needed
cell.myButton.setTitle("New Button Text", for: .normal)
cell.myButton.tintColor = .green
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "MySecondCell", for: indexPath) as! MySecondTableViewCell
// Configure cell if needed
cell.myTextField.backgroundColor = .red
return cell
}
}
}
Example UITableViewCell subclass:
class MyFirstTableViewCell: UITableViewCell {
#IBOutlet weak var myButton: UIButton!
}
Result:
I have a UITableView, which as an example contains dynamic cells I create based on the content of an array.
I can populate these using the count of the array and indexPath to render a cell per item. I am happy with this and it works well.
I would like to try now and create static cells programmatically.
Immediately however I am stumped, how do I create this? I'm currently overriding numberOfRowsInSection and cellForRowAt indexPath as follows:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellID", for: indexPath) as! ProfileCell
cell.rowContent.text = items[indexPath.row]
return cell
}
I suspect my first mistake is dequeueReusableCell and would really appreciate any help.
If I understood your question, you want to add a static cell in a tableView that contains dynamic cells.
If that is the case, you could hardcode this, increasing the return value here:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count+1
}
In this case, you want to add just one static cell.
In cellForRowAtIndexPath, you should define where you want to add this static cell. In the example below, it would be the first cell:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
//should change StaticCell to the static cell class you want to use.
let cell = tableView.dequeueReusableCell(withIdentifier: "staticCell", for: indexPath) as! StaticCell
//hardcore attributes here, like cell.rowContent.text = "A text"
return cell
}
let row = indexPath.row-1
let cell = tableView.dequeueReusableCell(withIdentifier: "cellID", for: indexPath) as! ProfileCell
cell.rowContent.text = items[row]
return cell
}
It's basically about shifting the items[row] according to the number of static cells you want to use.
I'm not sure if that will work, but would be my first guess (according to my experience about that). Try this and tell me if it worked :)
According to my other thread, I have a signal SIGABRT error in my code. The code consists of:
A Storyboard with 1 TableView and 1 Table Cell included
A custom Tablecell class (TableViewCell_Prototyp), which is connected to the Storyboard by (strong) IBoutlets.
A View Controller class, which is described in the following.
My ViewController class is connected to 3 super-classes: UIViewController, UITableViewDelegate, UITableViewDataSource. In addition, there are 3 functions implemented. Can somebody find the mistake ?
override func viewDidLoad() {
super.viewDidLoad()
}
i don't know what happens here. It was written by default so it should not cause any errors.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
i just want one table cell to be returned.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell (withIdentifier: "picCell") as! TableViewCell_Prototyp
cell.shoutPic.image = #imageLiteral(resourceName: "Bildschirmfoto 2017-06-05 um 10.11.21")
cell.shoutText.text = "Hallooooooooo"
return cell
}
shoutPic and shoutText is connected to the TableViewCell_Prototype custom class by outlets.
When you have custom table cell classes registered for your tableView, it's better to dequeue them using dequeueReusableCell(withIdentifier:for:), like so:
let cell = tableView.dequeueReusableCell(withIdentifier: "picCell", for: indexPath) as! TableViewCell_Prototyp
If this is all of your code, I would check that your prototype cell's identifier matches "picCell" exactly and that its class is set to TableViewCell_Prototyp
I think #Gereon is right that you need to dequeue with a different method.
Having the class TableViewCell_Prototyp is not enough -- you need to set it to the cell in the Storyboard file. Click on the cell in the storyboard and set it in the Identity tab.
I'm trying to make a UITableView that can support having different objects/elements inside it. Specifically, these elements are a UITextField and UISwitch.
The first problem:
The elements do not show up. They are placed in the prototype cell, which is then constructed inside the class I have set up. I have verified that the cell setup is working because I can change the words on each cell, but there are no elements within the cell.
Here is the code that constructs my cells right now:
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return 1
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "EmailCell")
return cell
}
Second problem (which might be solved along with the first):
I have no way of accessing the information in each UITextField or each UISwitch. How can I get this information from all cells that exist?
Thanks in advance for the help!
There are multiple things wrong with your code.
For custom cells you need to implement a custom UITableViewCell subclass. Here is an example:
import UIKit
class EmailCell: UITableViewCell {
#IBOutlet var customTextField: UITextField!
#IBOutlet var customSwitch: UISwitch!
}
After that, open your Storyboard and select the prototype cell. Change it's class to EmailCell in the Identity Inspector.
Also make sure to connect your ui elements to the #IBOutlets created earlier. See this StackOverflow post if you need help with #IBOutlet.
In the next step, change your tableView(_:, cellForRowAt:) implementation like this:
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "EmailCell", for: indexPath) as! EmailCell
// Configure your custom ui elements however you want
cell.customTextField.text = "somestring"
cell.customSwitch.isOn = true
return cell
}
Make sure your cells have reuse identifiers and you're using
tableView.dequeueReusableCell(withIdentifier: -Your cell Id- , for: indexPath) as? -Your Cell Class-
in your cell for row at index datasource method
next you can add targets to your cell text field / switch by doing this in your cell for row at index datasource method
cell.-your switch / text field-.addTarget(self, action: #selector(valueChanged(_:)), for: .valueChanged)
and you should subclass a uitableview cell to add the property / iboutlets
class YourTableViewCell: UITableViewCell {
#IBOutlet weak var yourSwitch: UISwitch!
}