I created an XIB file and its controller to handle my reusable custom cells:
And set my XIB's class owner:
class WallTableCell: UITableViewCell {
//Outlets
func setupCell() {
setupLabels()
}
func setupLabels() {
self.title.text = "Sample Title"
self.postDescriptionLabel.text = "Sample Description"
}
}
I, currently have 2 storyboards, where the first one leads to the second one, using a NavigationView, the second one contains a TabBarView where the first of the items has a TableView.
Here, I added a UITableViewCell in the storyboard
The cell's class is set to:
And the controller's class is set to:
Where I register my Nib and try to use it on the cellForRowAt method:
class WallViewController: UIViewController {
#IBOutlet weak var messagesTable: UITableView!
override func viewDidLoad() {
loadNibForCells()
}
func loadNibForCells() {
let nib = UINib(nibName: "WallTableCell", bundle: nil)
messagesTable.register(nib, forCellReuseIdentifier: "wallCell")
}
}
extension WallViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let wallCell = messagesTable.dequeueReusableCell(withIdentifier: "wallCell") as? WallTableCell else {
return UITableViewCell()
}
wallCell.setupCell()
return wallCell
}
}
But still, when I run the app in the simulator, I keep getting an empty cell.
What should I do in order to display my custom XIB view inside my tableViewCell?
After following #Sh_Khan's answer and adding:
messagesTable.delegate = self
messagesTable.dataSource = self
The app crashes with an error:
Thread 1: Exception: "[<NSObject 0x600001628e70> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key postDescriptionLabel"
I believe it has to do with the fact that the TableViewCell inside the storyboard is empty rather than containing the components of the WallTableCell
Make sure to set dataSource and delegate if needed
messagesTable.register(nib, forCellReuseIdentifier: "wallCell")
messagesTable.delegate = self
messagesTable.dataSource = self
There is an error: Thread 1: Exception: "[<NSObject 0x600001628e70> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key postDescriptionLabel", so maybe you need to unbind the postDescriptionLabel and bind it from xib file to swift file again.
While I was missing to add my messagesTable's dataSource which was what Sh_Khan recommended, the solution for my error afterwards was that my reference outlets were pointing to "File's owner" rather than my File's outlets.
So, when selecting the custom TableViewCell (not the prototype) should be:
For some reason, instead of being "Title Label" on the right side, it was "File's owner"
And the prototype cell shouldn't contain any class attached to it.
Related
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.
I am running into a weird issue when creating a customHeader for my tableView. the error that I am receiving is the following:
nib must contain exactly one top level object which must be a UITableViewHeaderFooterView instance
I ran through my code and xib file as well as examples but I couldnt find anything wrong any ideas I am missing?
The header Xib was created with a regular view and then a sub view with a label, the class was created with the following:
CustomTableHeader Class:
class CustomTableHeader: UITableViewHeaderFooterView {
static var CustomTableHeaderIdentifier = "CustomTableHeader"
#IBOutlet weak var titleLabel: UILabel!
override awakeFromNib(){
super.awakeFromNib()
self.titleLabel.text = "Header"
}
override func prepareForReuse() {
super.prepareForReuse()
}
}
ViewController Class:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UINib.init(nibName: CustomTableHeader.CustomTableHeaderIdentifier, bundle: nil), forHeaderFooterViewReuseIdentifier: CustomTableHeader.CustomTableHeaderIdentifier)
}
//Other Methods
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: CustomTableHeader.CustomTableHeaderIdentifier) as? CustomTableHeader {
header.titleLabel.text = "Section 1"
return header
}
}
I figured out my issue! Just in case anybody ever runs into this, make sure that your first view is truly the only object in the XIB file. For example on mine I was trying to add a gesture to the header view and this is considered an object as well. If you want to add a gesture you can do so programmatically:
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(/* FUNCTION */)))
I wanna populate a dynamic tableview inside a static tableview cell, by the same class for both of these.
As you can see in the picture under the cell 'GRE Test Information'.
I'm using the code inside the the class named as MenuController, which is a tableview controller.
class MenuController: UITableViewController,MFMailComposeViewControllerDelegate {
#IBOutlet weak var tablle: UITableView!
var items = [String]()
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()
items = ["A "," BB "]
tablle.delegate = self
tablle.dataSource = self
self.tablle.registerClass(MainTableViewCell.self, forCellReuseIdentifier: "cellNew")
}
// Table Data Source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
-> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cellNew", forIndexPath: indexPath) as! MainTableViewCell
print("Aasim Khaan")
cell.customCell01.text = items[indexPath.row]
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
But it's not populating that at runtime, and says
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier cellNew - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
However I'm using the same identifier named as cellNew both in the code and storyboard.
Well after astonishing efforts regarding this one, I've found the solution.
Concerning the following:
Swift: TableView within Static UITableViewCell
Where the problem solver says : As far as I can determine by experimenting with this, you can't use the same UITableViewController as the data source and delegate of both table views. With a static table view, you're not supposed to implement the data source methods at all. The strange thing is, even if I disconnect the data source and delegate connections between my static table view and the table view controller, that table view still calls numberOfRowsInSection in my table view controller class. If I explicitly set the data source to nil in code, that stops it from calling the data source methods, but the embedded dynamic table view also fails to call them, so this structure doesn't work.
However, you can get around this by using a different object to be the data source and delegate of your embedded dynamic table view. Make an IBOutlet for your embedded table view, and set its data source and delegate to this new object (The class is DataSource in this example, and it's a subclass of NSObject).
I've modified my code in this way now :
import Foundation
import UIKit
import MessageUI
class DataSource: NSObject, UITableViewDataSource, UITableViewDelegate {
var items : [String] = ["GRE Test Structure ","GRE Score "]
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cellNew", forIndexPath: indexPath) as! MainTableViewCell
cell.customCell01.text = items[indexPath.row]
return cell
}
}
class MenuController: UITableViewController,MFMailComposeViewControllerDelegate {
#IBOutlet var tablle0: UITableView!
#IBOutlet weak var tablle: UITableView!
var dataSource = DataSource()
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()
tablle.delegate = dataSource
tablle.dataSource = dataSource
}
}
Now it works exactly fine.
in viewDidLoad
// First Register the UITableViewcell class from nib
let cellNib = UINib(nibName: "MainTableViewCell", bundle: bundle)
self.tableView.registerNib(cellNib, forCellReuseIdentifier:"cellNew")
Then Check with below screeshots
STEP 1: Select MainTableViewCell from Identity Inspector-Custom Class-Click Class Drop Down arrow.It shows you list.From that you can click the MainTableViewCell
STEP 2:Once you click that it shows the name with selected table view cell.
While the existing answers explain how you can do this, they don't address whether you should do this. From the example you provided, it seems that all you need is a single UITableView with multiple dynamic cell types. Each cell type can specify its contentInsets to indent the content as needed.
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'unable to dequeue a cell
with identifier cellNew - must register a nib or a class for the
identifier or connect a prototype cell in a storyboard'
However I'm using the same identifier named as cellNew both in the
code and storyboard.
You're getting this error because you are dequeing/retrieving the prototype cell from the wrong table!
The line in your cellForRowAtIndexPath should be:
let cell = tablle.dequeueReusableCellWithIdentifier("cellNew", forIndexPath: indexPath) as! MainTableViewCell
Having said that, even once that is working, asking a tableViewController to act as data source and delegate for both a static and a dynamic table causes problems later.
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.
Why I get this error?
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: '-[UITableViewController
loadView] loaded the "pB1-re-lu8-view-o7U-YG-E7m" nib but didn't get a
UITableView.'
here is my code:
class FriendListTableViewController: UITableViewController{
var objects = NSMutableArray()
var dataArray = [["firstName":"Debasis","lastName":"Das"],["firstName":"John","lastName":"Doe"],["firstName":"Jane","lastName":"Doe"],["firstName":"Mary","lastName":"Jane"]]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
let object = dataArray[indexPath.row] as NSDictionary
(cell.contentView.viewWithTag(10) as! UILabel).text = object["firstName"] as? String
(cell.contentView.viewWithTag(11) as! UILabel).text = object["lastName"] as? String
return cell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return false
}
my storyboard is like this:
I have face same issue once upon time, and So stupid mistake it was, I have subclass the UITableViewController, where I have added UITableView in UIViewController
From your storyboard Image, it may be solved if you use UIViewController instead of UITableViewController, Just try that way can solve your issue,like
class FriendListTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate
SWIFT 2 UPDATE
I tried #Viralsavaj's answer but it didn't work for me until I "hooked up" the tableView as an outlet from the storyboard in the ViewController.
like this: #IBOutlet weak var tableView: UITableView!
Also add this to your class as #Viralsavaj mentioned:
class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate
I used specifically this link help as well: How to reference tableView from a view controller during a segue
Just drag your tableView into the ViewController and make it an outlet. Then you can reference its properties such as self.tableView.reloadData().
This property as well as others that referenced the tableView were giving me errors until I added the tableView as a referencing outlet.
Hope this helps those in the future.
I had this issue with Swift 2, Xcode 7.2, I changed a View Controller I dragged to my Storyboard to a custom UITableViewController class, I then dragged a Table View onto the View Controller. I didn't realize I placed it as a child of the View that was part of the original View Controller I dragged onto the Storyboard.
I simply deleted the View and added the Table View again as the first child of the Super View.