IBOutlet not accessible in awakeFromNib in UITableViewCell - ios

As far as I can tell, I have set up everything correctly (register and dequeue). However, when I try and access outlets in awakeFromNib(), I get
'Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file ....MechEntryCell.swift, line 18'
Which is weird, since other UITableViewCells doing similar things seem to work fine - and look identical to me.
The code failing:
class MechEntryCell: UITableViewCell {
#IBOutlet weak var mechName: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
mechName.text = "Hello" <<<<<<< CRASHES
}
func setDamageOption(_ damageOption: AdditionalDamageOption) {
mechName.font = UIFont.boldSystemFont(ofSize: mechName.font.pointSize)
mechName.text = damageOption.label <<<< WORKS FINE IF ABOVE CRASH REMOVED
}
}
which has a .xib file where the label has been correctly linked:
And I also register and dequeue:
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: MechEntryCell, bundle: nil), forCellReuseIdentifier: "MechEntryCell")
........
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tempDamageOptions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: MechEntryCell.identifier) as? MechEntryCell {
cell.setDamageOption(tempDamageOptions[indexPath.item])
return cell
}
return UITableViewCell()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
Special Note:
When I set the label in the tableView(cellforRowAt:) method, then it works fine - so the IBOutlet DOES eventually link. What am I doing wrong?

Just want to add another example that might easily/accidentally happen to someone. In this case, had accidentally put the cell class on the ContentView, (rather then should have been left as simply UIView). In code, the cell's awakeFromNib was getting called twice. The first time the outlets were set, and in the second mysterious call, the outlets were nil.

I come back in shame with the solution.
somewhere during setup, I accidentally set the root view's custom class to that of the MechEntryCell. Removing this solved the issue.

Related

Collection or TableView inside a tableView cell is broken in Mac Catalyst

I'm using UITableViews with cells containing either a UICollectionView or UITableView in them. The cell's height is dependent on the content of the table/collectionView inside it. Think of it like the sections in the Settings app.
This all has been working fine for 2 years now (on iPhone/iPad/Mac running Mac Catalyst), but this week's macOS 12.5 release has caused every cell like this to have some items seemingly missing. However, clicking on a missing item will still have the correct action work, or moving the mouse around/above the missing cells, they will cause the correct content to appear.
Here's a code snippet of a tableView cell with a UITableView in it:
class SettingsCell: UITableViewCell, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var contentTable: UITableView!
//Height constant of tableView in cell
#IBOutlet weak var contentTableHeightConstraint: NSLayoutConstraint!
private var _type: SettingType = .unknown
override func awakeFromNib() {
super.awakeFromNib()
contentTable.delegate = self
contentTable.dataSource = self
contentTable.isScrollEnabled = false
contentTable.register(UINib(nibName: "SettingsRowCell", bundle: nil), forCellReuseIdentifier: "SettingsRowCell")
contentTable.estimatedRowHeight = 45.0
contentTable.rowHeight = UITableView.automaticDimension
layoutIfNeeded()
contentTableHeightConstraint.constant = contentTable.contentSize.height
}
//This is called in the cellforRowAt method in the parent table where it chooses which type of settings section to show
func updateWithType(type: SettingType) {
_type = type
contentTable.reloadData()
layoutIfNeeded()
contentTableHeightConstraint.constant = contentTable.contentSize.height
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if type == .general {
return generalArray.count
} else if type == .notifications {
return notifArray.count
}
return 0
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 45
}
}
The issue seems to steam from a tableView or collectionView being inside a tableViewCell because happens to all my cells like this.
I've been digging through the code for days, and nothing is out of the ordinary. I never hide any of the cell's content in any scenario, so this isn't the normal reusable cell issue where the content needs to be set back to being visible.
Does anyone know of the fix this or has any pointers? Also, these cells work as normal on iPhone/iPad. I've been trying to figure this out for a long time, and any help would be greatly appreciated!

UITableView.reloadData() is not refreshing tableView. No error message

I've already looked at the post UITableView.reloadData() is not working. I'm not sure that it applies to my situation, but let me know if I'm wrong.
My app has a tableView. From the main viewController I am opening another viewController, creating a new object, and then passing that object back to the original viewController, where it is added to an array called timers. All of that is working fine. However, when I call tableView.reloadData() in didUnwindFromNewTimerVC() to display the updated contents of the timers array, nothing happens.
NOTE: I have verified that the timers array is updated with the new object. Its count increments, and I can access its members. Everything else in didUnwindFromNewTimerVC() executes normally. The tableView just isn't updating to reflect it.
Here is my code:
import UIKit
class TimerListScreen: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tabelView: UITableView!
var timers = [Timer]()
let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
tabelView.delegate = self
tabelView.dataSource = self
let tempTimer = Timer(timerLabel: "temp timer")
timers.append(tempTimer)
}
#IBAction func didUnwindFromNewTimerVC(_sender:UIStoryboardSegue){
guard let newTimerVC = _sender.source as? newTimerVC else{return}
newTimerVC.timer.setTimerLabel(timerLabel: newTimerVC.timerLabel.text!)
timers.append(newTimerVC.timer)
tableView.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tabelView.dequeueReusableCell(withIdentifier: "TimerCell", for: indexPath) as? TimerCell{
let timer = timers[indexPath.row]
cell.updateUI(Timer: timer)
return cell
}else{
return UITableViewCell()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return timers.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 78
}
}
Thank you
Please note the spelling. There are two table view instances: the outlet tabelView and a (pointless) instance tableView.
Reload the data of the outlet
tabelView.reloadData()
and delete the declaration line of the second instance let tableView ....
However I'd recommend to rename the outlet to correctly spelled tableView (you might need to reconnect the outlet in Interface Builder).
And force unwrap the cell
let cell = tabelView.dequeueReusableCell(withIdentifier: "TimerCell", for: indexPath) as! TimerCell
and remove the if - else part. The code must not crash if everything is hooked up correctly in IB.

Fatal error (found nil) while loading stock tableView

I made basic tableView inside ViewController and while loading I get
fatal error: unexpectedly found nil while unwrapping an Optional value
Which points to tableView.delegate = self (by points to I mean this line is highlighted in green colour in Xcode). Here's full code:import UIKit
import UIKit
class FAQViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UINavigationControllerDelegate, SWRevealViewControllerDelegate {
#IBOutlet var menuButton: UIBarButtonItem!
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
if revealViewController() != nil {
//I have SWRevealController that slides viewController from Left side
}
}
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("FAQ") as! FAQTableViewCell
cell.questionLabel.text = "Here goes Question"
cell.answearLabel.text = "This is answear"
return cell
}
}
From what I can see from your example, you look to be setting things up using a storyboard, but since the class is a UIViewController and not a UITableViewController, I think your connections are not wired up correctly.
I would check in the debugger to make sure tableView is not nil and to check in the storyboard to make sure that the connections look OK.
You can also wire up the tableViews dataSource and delegate in storyboard by right clicking on the tableView, and then dragging from the circle across from dataSource or delegate to the view controller associated with your storyboard scene (i.e. the first icon in the hierarchy right below the scene name in the storyboard file).
Happy to clarify if this does not make sense...
Check the following:
check your cell reuse identifier specified in the .storyboard, Xib, or in code, and ensure that it is correct when dequeuing.
otherwise it will give fatal error, the app crashes.
You have declared the tableView as implicitly unwrapped. That means this has to be initialized before assigning value to this. I guess you have just declared the tableView but not wired to the story board.
I you do not want that way declare it as optional and initialize it before setting the datasource and delegate.

Swift Custom UITableViewCell not displaying data

I am new to Swift, and iOS development in general. I am attempting to create a custom UITableViewCell. I have created the cell in my main storyboard on top of a UITableView that is inside a UIViewController. When I loaded one of the default cells, I was able to populate it with data. However, now that I am using a custom cell, I cannot get any data to appear in the table. I have gone through all kinds of tutorials and questions posted on the internet, but I can't figure out why it is not working. Any help would be appreciated.
Here is my code for the UIViewController that the tableview resides in.
import UIKit
class FirstViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tblView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//self.tblView.registerClass(UITableViewCell.self, forCellReuseIdentifier : "Cell")
self.tblView.registerClass(CustomTableViewCell.self, forCellReuseIdentifier : "Cell")
tblView!.delegate = self
tblView!.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataMgr.data.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : CustomTableViewCell = self.tblView.dequeueReusableCellWithIdentifier("Cell", forIndexPath : indexPath) as! CustomTableViewCell
var values = dataMgr.data[indexPath.row]
cell.newTotalLabel?.text = "\(values.newTotal)"
cell.winLoseValueLabel?.text = "\(values.newTotal - values.currentTotal)"
cell.dateLabel?.text = "5/17/2015"
return cell
}
}
I have stepped through the program where it is assigning values to the cell variables. The variable 'values' is being populated with data, but when stepping over the assignment lines to the cell variables, I found that they are never assigned. They all remain nil.
When you make a custom cell in the storyboard, don't register the class (or anything else). Just be sure to give the cell the same identifier in the storyboard that you pass to dequeueReusableCellWithIdentifier:forIndexPath:.

UILabels in custom UITableViewCell never gets initialized

I'm having trouble creating custom table view cells in swift with Xcode6 (beta 4). More precisely I'm unable to access (unwrap) my custom UILabels inside the cell, since they never gets initialized.
Here's how I've got it all setup:
I've made a view in storyboard, which contains a table view with a prototype cell:
The view is hooked up to a class MyCoursesTableViewController, and the cell (with identifier courseCell) to CourseTableViewCell. I've pasted both classes below (with just the relevant bits of code):
MyCoursesTableViewController.swift
import UIKit
class MyCoursesTableViewController: NavToggleTableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(CourseTableViewCell.self, forCellReuseIdentifier: "courseCell")
}
override func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
return 1
}
override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell : CourseTableViewCell = tableView.dequeueReusableCellWithIdentifier("courseCell", forIndexPath: indexPath) as CourseTableViewCell
// cell is not nil
if let titleLabel = cell.titleLabel {
// never gets here
} else {
cell.textLabel.text = "Course Title"
}
return cell
}
}
The NavToggleTableViewController class is just a common baseclass I use for all view controllers and doesn't affect the result.
CourseTableViewCell.swift
import UIKit
class CourseTableViewCell: UITableViewCell {
#IBOutlet weak var courseIcon: UIImageView!
#IBOutlet weak var teacherIcon: UIImageView!
#IBOutlet weak var studentsCountIcon: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var studentsCountLabel: UILabel!
#IBOutlet weak var teacherLabel: UILabel!
init(style: UITableViewCellStyle, reuseIdentifier: String!) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
Below is a picture of how I've configured the cell in the utilities pane (in storyboard):
The problem arise inside the function tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> CourseTableViewCell when I want to access the UILabels. If I were to put something like cell.titleLabel.text = "Course Title" I get the following error message:
fatal error: unexpectedly found nil while unwrapping an Optional value
Where am I doing things wrong? Would appreciate any help, thanks!
As explained in the last answer in UITableView Using Swift , it is an Xcode bug. Add the following:
override func tableView(tableView:UITableView!, heightForRowAtIndexPath indexPath:NSIndexPath)->CGFloat {
return 44
}
(or whatever height you want) and you'll be ok.
I duplicated your code and verified the solution.
Apologies, I forgot something else: you must not registerClass:forCellReuseIdentifier. This is already done when you set up your table and prototype cell in interface builder.
Sorry to reply after so much time has passed but I tear my hair on this exact problem this whole weekend. This bug is still there on XCode 8.3.2 (8E2002), this is the latest one as I write these lines. I had the exact same problem as the OP and adding
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 44
}
did not worked for me. The only thing that worked for me was removing this from my viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
// REMOVE THIS v v v v v
self.tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "MyCustomCell")
// REMOVE THIS ^ ^ ^ ^ ^
// Do whatever you want hereā€¦
}
The heightForRowAt indexPath is ineffective, you don't need to use it to fix it.
I hope this helped someone too !

Resources