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
Related
I am trying to register UITableViewCell in viewdidload
self.tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "CustomTableViewCell")
In cellForRowAtIndex
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell") as! CustomTableViewCell
cell.productNameLabel.text = "Product"
cell.productNameLabel.textColor = UIColor.darkGray
return cell
}
Here it is crashing in cell.productNameLabel.text.
What is the purpose of registering cell? why it is crashing?
I want to reload data even if cell or table is not visible.
Crashreport :
See the Apple's comments which answers your query on the purpose of registering cell :
Prior to dequeueing any cells, call this method or the
register(_:forCellReuseIdentifier:) method to tell the table view how
to create new cells. If a cell of the specified type is not currently
in a reuse queue, the table view uses the provided information to
create a new cell object automatically.
This is the standard procedure I apply while working with Custom Cells (if you are using xib) :
Set cell's identifier in Xib's attribute inspector :
Register Xib :
self.tableTasks.register(UINib(nibName: "TaskCell", bundle: nil), forCellReuseIdentifier: "taskCell")
However, if you are not using Xib and creating custom cell using code only, then use registeCell :
self.tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "CustomTableViewCell")
Are you using a xib for this cell? If so, none of the outlets will be connected if you just register the class of the cell. You need to register the actual xib file, so that everything can be connected correctly when the cell is created. Have a look at
-(void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier
https://developer.apple.com/documentation/uikit/uitableview/1614937-registernib
My method for register cell.
Syntax sugar
protocol BSCellProtocol {
// For `registerCell`
static var NibName: String! { get }
// For `registerCell`, `dequeueCellWithType`, and `dequeueHeaderFooterWithType`
static var Identifier: String! { get }
}
extension UITableView {
func registerCell(_ type: BSCellProtocol.Type) {
let nib = UINib(nibName: type.NibName, bundle: nil)
let identifier = type.Identifier!
self.register(nib, forCellReuseIdentifier: identifier)
}
func dequeueCellWithType<T: BSCellProtocol>(_ type: T.Type) -> T {
let cell = self.dequeueReusableCell(withIdentifier: type.Identifier) as! T
return cell
}
func dequeueCellWithType<T: BSCellProtocol>(_ type: T.Type, index: IndexPath) -> T {
let cell = self.dequeueReusableCell(withIdentifier: type.Identifier, for: index) as! T
return cell
}
}
Usage
class MyCustomCell: UITableViewCell, BSCellProtocol {
static var NibName: String! = "MyCustomCell"
static var Identifier: String! = "cellIdentifier_at_Xib"
#IBOutlet weak var lblTitle: UILabel!
// other IBOutlet components
}
// In ViewController, register cell
tableView.registerCell(MyCustomCell.self)
// dequeue cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// cell is `MyCustomCell` instance
let cell = tableView.dequeueCellWithType(MyCustomCell.self)
// configure cell ...
// ....
return cell
}
I had the same problem. I also was not using XIB for cell. My view was not connected to View in File's Owner Outlets. Maybe this info will help someone.
ViewControllerA will be pushing to ViewControllerB. ViewcontrollerB is a UITableView that has a custom UITableViewCell that is registered upon load.
Here is my custom UITableViewCell:
override func awakeFromNib() {
super.awakeFromNib()
// Here is me attempting to pass my data from my view controller
someButton.text = stringFromViewController
}
Here's my UITableView:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let customCell = CustomCell()
customCell.stringFromViewController = "Some Text"
self.performSegueWithIdentifier("ViewControllerB", sender: nil)
}
Whenever I attempt this the string comes back as empty. I'm not sure if this has to do with the fact that I am trying to pass this data to the UITableViewCell instead of the UITableView that contains the custom cell. I'd like to pass the data to my custom cell class.
You going about this the wrong way.
your tableView is reloading automatically upon loading controller B.
you should pass the data to the cell in tableView:CellForRow: method.
you can also call tableView.reloadData() once you assign the value from controller A.
here's an example:
class ControllerB {
var tableView: UITableView! //IBOutlet?
var stringFromA: String? {
didSet {
self.tableView.reloadData()
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("UITableViewCell")
cell.stringFromA = self.stringFromA
return cell
}
Because awakeFromNib() initilizes even before you initilize the cell.
instead of assigning it in this method use this in didSelectRowAtIndexPath
let customCell = CustomCell()
customCell.someButton.setText("Some Text", forState: .Normal)
self.performSegueWithIdentifier("ViewControllerB", sender: nil)
My understanding of your question is that clicking on a row from View Controller A will push View Controller B and you want to pass some data to the custom cell in View Controller B. you have to use prepareForSegue to pass the data.
in view controller b, set a variable, and in your case, a bool.
class ViewControllerB {
var boolValue: Bool?
}
then in didSelectRowAtIndexPath in ViewControllerA you perform the segue
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("ViewControllerB", sender: nil)
}
Then you have to perform prepareForSegue according to the row being selected
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if segue.identifier == "ViewControllerB" {
//if you need to know which row is select, use indexPath.row
if let indexPath = tableView.indexPathForSelectedRow {
let controller = segue.destinationViewController as! ViewControllerB
//send the details that you want, i.e
if indexPath.row < 5 {
controller.boolValue = true
} else {
controller.boolValue = false
}
}
}
once the segue is called, you pass the data through prepareForSegue, then you can pass the information to the cell using cellForRowAtIndexPath method.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath) as! CustomTableViewCell
//pass the boolValue from viewcontrollerB to anotherBoolValue in custom cell
cell.anotherBoolValue = boolValue
// Configure the cell...
return cell
}
I agree with #Lirik
have a property observer on you controllerB's property and reload the table view, then in your cellForRowAtIndexPath(::)
cast the dequeued cell to your custom cell type so that you can access all the function of your cell
let cell:CustomCell = tableView.dequeueReusableCellWithIdentifier("UITableViewCell") as! CustomCell
cell.someBotton.text = stringFromViewControllerA
I agree with #Lirik
have a property observer on you controllerB and reload the table view, then in your cellForRowAtIndexPath(::)
cast the dequeued cell to your custom cell type so that you can access all the function of your cell
let cell:CustomCell = tableView.dequeueReusableCellWithIdentifier("UITableViewCell") as! CustomCell
I'm building an iOS application with Swift 2 that uses custom table view cells, with additional labels, image views, etc. (let's call the class CustomTableViewCell). I've made the class-storyboard connections to every subview and assigned an identifier to the cell. I've mocked the data and tried to run the application to check that the cell is properly mapped, and it looks ok.
The problem is that I cannot treat a dequeued cell as a CustomTableViewCell to test the value of its properties. When I downcast the cell returned from tableView(tableView, cellForRowAtIndexPath: indexPath) all custom property values turns into nil and my tests fail.
Here's my code:
MyViewControllerTests.swift
func testShouldConfigureTableViewCellToDisplayNotification() {
// Given
sut.tableView = TableViewSpy()
let items = [ <some items to display> ]
sut.displayedItems = items
// When
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
let cell = viewController.tableView(viewController.tableView, cellForRowAtIndexPath: indexPath) as! CustomTableViewCell
// Then
XCTAssertEqual(cell.detailLabel?.text, "foo", "A properly configured table view cell should display the notification detail")
XCTAssertEqual(cell.titleLabel?.text, "Bar", "A properly configured table view cell should display the notification title")
XCTAssertEqual(cell.dateLabel?.text, "15/04/2016", "A properly configured table view cell should display the notification date")
}
MyViewController.swift
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "CustomTableViewCell"
let displayedItem = displayedItems[indexPath.row]
var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? CustomTableViewCell
if cell == nil {
cell = CustomTableViewCell(style: .Value1, reuseIdentifier: cellIdentifier)
}
cell!.dateLabel?.text = displayedItem.date
cell!.detailLabel?.text = displayedItem.detail
cell!.titleLabel?.text = displayedItem.title
return cell!
}
CustomTableViewCell.swift
class CustomTableViewCell: UITableViewCell {
// MARK: Properties
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var detailLabel: UILabel!
#IBOutlet weak var titleLabel: 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
}
}
Try replacing
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "CustomTableViewCell"
let displayedItem = displayedItems[indexPath.row]
var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? CustomTableViewCell
if cell == nil {
cell = CustomTableViewCell(style: .Value1, reuseIdentifier: cellIdentifier)
}
cell!.dateLabel?.text = displayedItem.date
cell!.detailLabel?.text = displayedItem.detail
cell!.titleLabel?.text = displayedItem.title
return cell!
}
with
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "CustomTableViewCell"
let displayedItem = displayedItems[indexPath.row]
var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as! CustomTableViewCell
cell.dateLabel.text = displayedItem.date
cell.detailLabel.text = displayedItem.detail
cell.titleLabel.text = displayedItem.title
return cell
}
If you have connected your cell correctly ins storyboard then this should work. Otherwise check that you have correctly assigned all IBOutlets for your cell.
If something doesn't work please check the following:
1) Select your cell in the storyboard.
2) In the right column open Identity Inspector (3rd tab at the top). Make sure that the class of your cell is set to CustomTableViewCell.
3) In the Attributes Inspector (4th tab) make sure that cell identifier is correctly spelled.
4) In Connections inspector (last tab) assign all of the IBOutlets of your cell which you have defined.
From your code sample looks like you didn't registered your cell for reusing.
tableView.registerClass(CustomTableViewCell.self, forCellReuseIdentifier: "CustomTableViewCell")
I am trying to implement a UISearchController (not UISearchDisplayController which is deprecated)
I face a ridiculously time eating issue.
When I try to dequeueResusableCellWithIdentifier it doesn't works with my CellAutocompletion (UITableViewCell subclassing)
Like this :
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let item = filteredProducts[indexPath.row]
let cell:CellAutocompletion = self.tableView.dequeueReusableCellWithIdentifier("suggestionCell") as! CellAutocompletion
cell.itemLabel?.text = item.item_name;
return cell
}
This throw me fatal error: unexpectedly found nil while unwrapping an Optional value
BUT when I do this :
let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "suggestionCell");
It works. So with a Default iOS cell, it works, with my custom cell not. Any idea?
I think I ask the wrong tableview but considering I am inside a TableviewController included inside UISearchController which is used by another VC, I am lost.
My main VC which instanciate my searchController and the TableViewController where I have issue.
//ViewDidLoad
resultsTableController = ProductResultTabController();
resultsTableController.tableView.delegate = self;
self.searchController = UISearchController(searchResultsController: resultsTableController);
searchController.searchResultsUpdater = self;
searchController.dimsBackgroundDuringPresentation = true;
searchController.searchBar.sizeToFit()
tableView.tableHeaderView = searchController.searchBar
My CustomCell Class
class CellAutocompletion:UITableViewCell{
#IBOutlet weak var itemLabel: UILabel!
#IBOutlet weak var parentItemLabel: UILabel!
}
ProductResultTabController (subclass of TableViewController)
class ProductResultTabController: UITableViewController {
var filteredProducts = [ITEM]();
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredProducts.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let item = filteredProducts[indexPath.row]
let cell:CellAutocompletion = self.tableView.dequeueReusableCellWithIdentifier("suggestionCell") as! CellAutocompletion!
cell.itemLabel?.text = item.item_name;
return cell
}
}
As I understand, your ProductResultTabController lives in the storyboard with the cell prototype. As a consequence, call resultsTableController = ProductResultTabController() doesn't do the most essential part of creating a TableViewController, which is registering your tableView: for a class or nib. You could create your cell in a nib and call tableview.registerNib: in your PRTC viewDidLoad:, but there is an other, slightly more convenient way:
Create your PRTC in the storyboard and prototype the cell (I believe you've already done that)
In SB, go to attributes inspector and give the controller a storyboard ID
3 - In your main VC's viewDidLoad:, replace resultsTableController = ProductResultTabController()`by the following:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
resultsTableController = storyboard.instantiateViewControllerWithIdentifier(myStoryboardID)
So I have the weirdest thing;
I am looping a tableView in order to iterate over all cells. It works fine with less than 5 cells, but crashes with "unexpectedly found nil" for more cells. Here's the code:
for section in 0..<tableView.numberOfSections {
for row in 0..<tableView.numberofRowsInSection(section) {
let indexPath = NSIndexPath(forRow: row, inSection: section)
let cell = tableView?.cellForRowAtIndexPath(indexPath) as? MenuItemTableViewCell
// extract cell properties
The last line is the one that gives the error.
Any thoughts?
Because cells are reused, cellForRowAtIndexPath will give you cell only if cell for given indexPath is currently visible. It is indicated by the optional value. If you want to prevent from crash, you should use if let
if let cell = tableView?.cellForRowAtIndexPath(indexPath) as? MenuItemTableViewCell {
// Do something with cell
}
If you want to update values from cell, your cells should update the dataSource items. For example you can create delegate for that
protocol UITableViewCellUpdateDelegate {
func cellDidChangeValue(cell: UITableViewCell)
}
Add delegate to your cell and suppose we have a textField in this cell. We add target for the didCHangeTextFieldValue: for EditingDidChange event so it is called every time the user types somethink in it. And when he do, we call the delegate function.
class MyCell: UITableViewCell {
#IBOutlet var textField: UITextField!
var delegate: UITableViewCellUpdateDelegate?
override func awakeFromNib() {
textField.addTarget(self, action: Selector("didCHangeTextFieldValue:"), forControlEvents: UIControlEvents.EditingChanged)
}
#IBAction func didCHangeTextFieldValue(sender: AnyObject?) {
self.delegate?.cellDidChangeValue(cell)
}
}
Then in cellForRowAtIndexPath you add the delegate
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCellIdentifier", forIndexPath: indexPath)
cell.delegate = self
return cell
}
And finally we implement the delegate method:
func cellDidChangeValue(cell: UITableViewCell) {
guard let indexPath = self.tableView.indexPathForCell(cell) else {
return
}
/// Update data source - we have cell and its indexPath
}
Hope it helps