I have designed a custom cell in xib. And created a class for that as well. The code for that class is as given below-
class ProjectsCell : UITableViewCell {
#IBOutlet var projectNameLabel: UILabel! //This is outlet to which I will assign value.
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
}
}
Now I have a view controller there I am trying to access this cell. In storyboard I have given reusable identifier "Cell". Now I am using this cell like the following code-
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as ProjectsCell
let project = projects[indexPath.row]
cell.projectNameLabel?.text = project.ProjectName //********* Here I am getting exception for projectNameLabel.
return cell
I think that label is coming null. I have tried the following approach also but that is also not working.
var cell: ProjectsCell = tableView.dequeueReusableCellWithIdentifier("Cell") as ProjectsCell
tableView.registerNib(UINib(nibName: "ProjectsCell", bundle: nil), forCellReuseIdentifier: "Cell")
cell = tableView.dequeueReusableCellWithIdentifier("Cell") as ProjectsCell
What can be the issue if anyone has faced this same issue.
Your custom cell should inherit from the class UITableViewCell. So the class would look like this.
class ProjectsCell: UITableViewCell {
#IBOutlet var projectNameLabel: UILabel! //This is outlet to which I will assign value.
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
}
}
If you have it like this it should work. Because i am doing it like that in my Apps. For the future if you create a class you can use the 'File -> New Files...' menu. There you can select coca touch class and specify the class you want to inherit from and xcode will add all necessary functions.
You have dequeued the custom cell but not initialised it with the following method.
Besides this you also have to set your custom class as the sub-class of UITableViewCell this is because you are getting the null value for the cell.
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStylePlain reuseIdentifier: "Cell")
}
I solved the problem using the following code-
var array = NSBundle.mainBundle().loadNibNamed("ProjectsCell", owner: self, options: nil)
var cell = array[0] as ProjectsCell
let project = projects[indexPath.row]
cell.nameLabel?.text = project.Name
return cell
Thanks everyone for contributing. :)
Maybe it set the dataSource and delegate :
(source: icodeblog.com)
Related
There are many, many questions on this site and others addressing the issue of getting a Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value from trying to set an interface element in a custom table view cell.
How can I prevent the interface elements from being nil when it's time to set them?
I've done my homework and checked out the answers to these related questions.
I've made sure I don't register the class in the viewDidLoad, as I'm using a UITableViewController.
The UITableViewController is referenced properly in the Main.storyboard file.
The cells in the UITableViewController are given the proper reuseIdentifier and class.
My dequeueReusableCell call to the tableView has the proper identifier, matching the one in the storyboard.
I've broken the nib's outlets and reconnected them.
I'm setting up a UITableViewController, "FindTableViewController."
class FindTableViewController: UITableViewController {
var services = Fetch().getServices()
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
/*Sections & rows defined here*/
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Monk", for:indexPath) as! Monk
let service = services[indexPath.row]
cell.set(service)
return cell
}
}
In another file, I define the custom cell Monk.
import UIKit
class Monk: UITableViewCell {
#IBOutlet weak var titleImage: UIImageView!
//other outlets defined here
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
}
func set(_ service:Service) {
if let imgv = self.titleImage {
imgv.image = service.image
} else {
print(self.titleImage)
}
self.titleLabel.text = service.title
/*other elements set here*/
}
}
I have a feeling that I did something really, really stupid.
I've set the custom class name to Monk, and also the cell identifier to Monk, because I didn't want that confusion to be the source of the issue and making the names different didn't solve the problem.
I've even printed the values in the Service object and they match what was expected.
if let imgv = self.titleImage {
imgv.image = service.image
} else {
print(self.titleImage)
}
Prints "nil" and the error is thrown on the next line,
self.titleLabel.text = service.title
I also set the identifier and class of the nib to "Monk".
This is something simple.
If you are using a custom NIB for your cell, then you do need to register it.
Something like this in viewDidLoad:
let myNib = UINib(nibName: "MonkCell", bundle: nil)
tableView.register(myNib, forCellReuseIdentifier: "Monk")
This is unnecessary if you choose to layout the cell directly in the storyboard for your view controller.
I am trying to find out if it is possible to subclass TWTRTweetTableViewCell from the TwitterKit library. So far I have my custom cell class inherit from TWTRTweetTableViewCell. The xib has a UIView in it which has an outlet to the cell class and the UIView class is set to
TWTRTweetView. Like this-
class UserTweetViewCell: TWTRTweetTableViewCell {
#IBOutlet weak var tweetViewCustom: TWTRTweetView!
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
}
}
The cell's class in property inspector is set to UserTweetViewCell and the UIVIew's class in the cell is set to TWTRTweetView.
In the main view controller I have this
tableView.register(UserTweetViewCell.self, forCellReuseIdentifier: tweetTableReuseIdentifier)
and then
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let tweet = tweetsarr[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: tweetTableReuseIdentifier, for: indexPath) as! UserTweetViewCell
cell.tweetViewCustom.showActionButtons = false
cell.tweetViewCustom.linkTextColor = UIColor(red:0.12, green:0.53, blue:0.90, alpha:1.0)
cell.tweetViewCustom.configure(with: tweet as? TWTRTweet)
cell.tweetViewCustom.theme = .light
cell.tweetViewCustom.delegate = self
return cell
}
However, i get an error at line cell.tweetViewCustom.showActionButtons = false and the error is Unexpectedly found nil while unwrapping an Optional value. What am I missing here?
I finally did it and it's working like a charm. The trick is not to subclass TWTRTweetTableViewCell but instead just subclass a regular UITableViewCell and use a TWTRTweetView inside of it. Which is basically what TWTRTweetTableViewCell does, it has tweetView property which is essentially an IBOutlet of type TWTRTweetView. The custom cell Nib should contain a UIView with TWTRTweetView set as it's class in the identity inspector. Here goes the code-
class CustomTweetCell: UITableViewCell{
#IBOutlet weak var customTweetView: TWTRTweetView!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func configureCell(with tweet: TWTRTweet){
self.customTweetView.showActionButtons = false
self.customTweetView.configure(with: tweet)
}
}
For the cell's height, the following needs to be done for the tableview-
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let tweet = tweets[indexPath.row]
let tweetheight = TWTRTweetTableViewCell.height(for: tweet as! TWTRTweet, style: .compact, width: self.view.frame.width, showingActions: false) + 30 //this 30 should be the height of any additional views that you put in the cell Nib file
return tweetheight
}
NOTE: Its extremely important to have autolayout constraints enabled within the tableview cell with the TWTRTweetView and any other views that you may have and also make sure the Table view cell row height is set to Default or blank in the cell's Size inspector.Failing to do so will mess up the tweet view height and will cause undesirable results.
I wanted to Subclass TWTRTweetTableViewCell so that I could add the likes count, retweets count, reply button etc. so far it hasn't worked. So next I am going to give it a try Subclassing TWTRTweetView and use that in the tableview cell instead. I think I have tried it once with partial success. The challenge is the tweet height
This is how I am calculating the tweet height in Objective-c:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
TWTRTweet * tweet = self.tweets[indexPath.row];
if (self.tweets.count > indexPath.row) {
[self.prototypeCell configureWithTweet:tweet];
}
CGFloat tweetHeight = [CustomTweetTableViewCell heightForTweet:tweet style:TWTRTweetViewStyleCompact width:[tableView bounds].size.width showingActions:YES];
self.tweetHeights[indexPath.row] = [NSNumber numberWithFloat:tweetHeight];
return tweetHeight;
}
Usually, We use the dequeueReuseableCellwithIdentifier method in ViewController class but I want to use this method in the UITableViewCell.I have tried but I got the exception like this.
fatal error: unexpectedly found nil while unwrapping an optional value
ViewController Class:
#IBOutlet var tableView: UITableView!
var tableData:[songData] = [songData]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.backgroundColor = UIColor.orangeColor()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = TableViewCell()
cell.datas()
return cell
}
}
TableViewCell Class:
#IBOutlet var text1: UILabel!
#IBOutlet var text2: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func datas(){
let vc = ViewController()
let tableData = vc.tableData
print(tableData)
let tableview = vc.tableView
let indexpath:NSIndexPath = NSIndexPath()
let cell = tableview.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexpath) as! TableViewCell //The fatal error is showing exactly at this line.
let artistAndAlbum = tableData[indexpath.row]
cell.text1.text = artistAndAlbum.country
cell.text2.text = artistAndAlbum.currency
tableview.reloadData()
}
I need to customize my table data in the TableViewCell class.If it is possible help me or else why it is not possible?
You're going about this the wrong way. It honestly doesn't make any sense for your table cell subclass to be creating itself. It does make sense, however, for your cell subclass to be passed data and for it to populate itself from that.
You should have your view controller dequeue the cell as normal and then change your table cell function to take some data as a parameter and update itself.
In your view controller:
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerNib(UINib(nibName: "INSERT_NIB_NAME", bundle: nil), forCellReuseIdentifier: "Cell")
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TableViewCell
cell.updateWithData(tableData[indexPath.row])
return cell
}
If your cell is a prototype cell in the storyboard then you have to set the reuse identifier there instead of registering in viewDidLoad.
In your table cell:
func updateWithData(artistAndAlbum: songData) {
text1.text = artistAndAlbum.country
text2.text = artistAndAlbum.currency
}
In your view controller's viewDidLoad(), register the class with a reuse identifier.
tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: "ID")
Then your cellForRowAtIndexPath method can dequeue your custom cell.
tableView.dequeueReuseableCellWithIdentifier("ID", indexPath: indexPath)
This isn't just limited to view controllers. If you have a custom table view cell, then register the class for a reuse identifier wherever you setup the table view and then dequeue your custom cell with that identifier in its cellForRowAtIndexPath.
As a general rule of thumb, your view should not keep a reference to its view controller. The view shouldn't care about any view controllers or need to know what the view controller is doing. Either the entire table view and all of its workings should go in your view, hidden from the view controller, or you should keep all of your table view code in the view controller. This will make your life much easier.
Firstly you must set name for cell identifier
after it in cellForRowAtIndexPath method used this code:-
for custom cell
CustomCellTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CELL" forIndexPath:indexPath];
//-------------------------------------------------------------
for normal cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CELL" forIndexPath:indexPath];
Check your vc.tableView. It's probably nil
//The objectsAry is NSManagedObjects
//main view controller
var nowImg = UIImage(named: "img30.png")
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var NuTblCellInst = NuTableViewCell()
var maincell:UITableViewCell = self.mainList.dequeueReusableCellWithIdentifier("maincell") as NuTableViewCell
maincell.textLabel.text = self.objectsAry[indexPath.row].valueForKey("itemname") as? String
//NuTblCellInst.doImgLoad(nowImg!)
//uncomment the line above = fatal error: unexpectedly found nil while unwrapping an Optional value
// it doesn't matter if i use the method or use NuTblCellInst.btnWImg.setBackgroungImage directly (unwrapping error either way)
return maincell
}
// custom tableview cell class
class NuTableViewCell: UITableViewCell {
#IBOutlet weak var btnWImg: UIButton!
var nowImg = UIImage(named: "img30.png")
func doImgLoad(theImg: UIImage) {
btnWImg.setBackgroundImage(theImg, forState: UIControlState.Normal)
}
override func awakeFromNib() {
super.awakeFromNib()
//doImgLoad(nowImg!)
//uncomment the line above and it loads the image to the button no problem
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
I'm populating a reusable cell w/text as above, but not sure how to access the image (or background image) for a button placed in the table cell using storyboard where the image will vary with the table row.
From '... will vary with the table row,' it sounds like you're building a reusable template cell.
CustTableViewCell must be derived from UITableViewCell
Editing your template cell in the storyboard editor...
in the Identity Inspector, set the class to your custom class
create an outlet for the button in CustTableViewCell
In tableview:cellForRowAtIndexPath
create/dequeue maincell as a CustTableViewCell -- not UITableViewCell
set properties on the button via the outlet
return your instance (of course :-) )
cf custom uitableviewcells in storyboard
Pretty simple code:
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: BookTableViewCell = BookTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "BookCell")
println("ip: \(indexPath.row)")
cell.bookLabel.text = "test"
return cell
}
On the cell.bookLabel.text line I get this:
fatal error: unexpectedly found nil while unwrapping an Optional value
The BookTableViewCell is defined like this:
class BookTableViewCell: UITableViewCell {
#IBOutlet var bookLabel: 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
}
}
And bookLabel is correctly hooked up in a Prototype cell in the Storyboard. Why am I getting this error?
If you're using storyboard, make sure you don't have this line at the start of your file:
self.tableView.registerClass(CustomTableViewCell.self, forCellReuseIdentifier: "customCell")
It will overwrite the storyboard and as a result, the outlet links in the storyboard are ignored.
I was getting this error because I didn't have the identifier written in the Storyboard of the Custom Cell.
Also make sure it matches you code in:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("CustomTableCell") as CustomTableCell
...
}
Possibly that your view in Main.Storyboard lost its IBOutlet reference in ViewController file, just link it again.
When you create a view in code, its IBOutlet properties don't get hooked up properly. You want the version that you get back from dequeueReusableCellWithIdentifier:
let cell = tableView.dequeueReusableCellWithIdentifier("BookCell") as BookTableViewCell
Do not forget to register nib (tested with Swift3), e. g. inside override func viewDidLoad():
self.tableView.register(UINib(nibName: "BookTableViewCell", bundle: nil), forCellReuseIdentifier: "BookCell")
The reason why this question gets asked a lot is because it depends on how you setup your tableview and custom CellClass. Do you create your tableview in storyboard or programmatically? Do you create custom .xib Cells and custom Cell classes?
If you created your tableview programmatically and created custom .xib and Cell class here is the answer for Swift 4:
in viewDidLoad:
customTable.register(UINib(nibName: "NibName", bundle: nil), forCellReuseIdentifier: "NibNameIdentifier")
in cellforRowat:
let cell = tableView.dequeueReusableCell(withIdentifier: "NibName") as! ClassName
Note: In your cell .xib file make sure you set your identifier in the Attributes inspector ("NibNameIdentifier").
You need to check two things
1. Register cell with nib name in viewDidLoad
func viewDidLoad()
{
super.viewDidLoad()
listTableView.register(UINib.init(nibName: "ListProductCell", bundle: nil), forCellReuseIdentifier: "ListProductCell")
}
2. Create custom cell this way.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ListProductCell") as! ListProductCell
return cell
}
Swift 5
In same storyboard two class are there Class A and Class B, Class B contains tableview outlet, when i tried to push Class A to Class B it's crashed and show tableView outlet nil.
In class A i did navigation like below code.
let classBObj = ClassB()
self.navigationController?.pushViewController(classBObj, animated: true)
Then i realised my mistake and used below code and it's work perfectly.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let classBObj = storyboard.instantiateViewController(withIdentifier: "ClassB") as! ClassB
self.navigationController?.pushViewController(classBObj, animated: true)
In my case it was the way the Optional is unwrapped:
let cellId:String = "ConverterTableCell"
let cell: ConverterTableViewCell = (tableView.dequeueReusableCellWithIdentifier(cellId)! as? ConverterTableViewCell)!
Try doing this:
let cell = tableView.dequeueReusableCellWithIdentifier("BookCell", forIndexPath: indexPath) as! BookTableViewCell
and don't forget to set the reuse identifier in your storyboard
I am getting this error whenever I use reuse Identifer name different than the custom class name
unifid those names solve it for me
I encounter this error, if I put UITapGestureRecognizer in a custom UITableViewCell on the storyboard.(Xcode version is 8.3).
In Swift 5
First I tried registering my UITableViewCell in viewdidload() using class and identifier which I have mentioned below but it did not work for me.
self.tableView.registerClass(MyCustomTableViewCell.self, forCellReuseIdentifier: "customCell")
Solution
Then I registered my UITableViewCell using Nib name and it worked for me
Register your cell in viewdidload() using Nib name
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
//register your table view cell in Viewdidload() using Nib Name
tableView.register(UINib.init(nibName: "MyCustomTableViewCell", bundle: nil), forCellReuseIdentifier: "customCell")
}