I'm developing a UIViewController with 2 TableView, following Swift specification I created a class with UITableViewDelegate and UITableViewDataSource interfaces for managing TableViews.
class CurrencyController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var valute = [NSManagedObject]()
var categoria = [NSManagedObject]()
//....
}
I wrote the func to support rearranging TableView:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if tableView == self.tabCurrency{
//
let cell = tableView.dequeueReusableCellWithIdentifier("currencyRow", forIndexPath: indexPath) as! CurrencyCell
let thisElem = valute[indexPath.row]
cell.curName.text = thisElem.valueForKey("valuta") as! String? //This code generates the exception
let elem = thisElem.valueForKey("cambio") as! Double? //This code generates the exception
cell.rateCur.text = "\(elem!)"
return cell
}else{
//
let cell = tableView.dequeueReusableCellWithIdentifier("categoryRow", forIndexPath: indexPath) as! CategoryCell
let thisElem = categoria[indexPath.row]
cell.catName.text = thisElem.valueForKey("cat_name") as! String? //This code generates the exception
return cell
}
}
This code generates unexepctedly found nil while unwrapping an Optional value but using the PO command I saw that variable didn't contain nil values...
Any idea? Thank you in advance
UPDATE:
I wrote the declaration of "valute" and "categoria" objects.
It seems:
this.valueForKey("cambio")
should be:
thisElem.valueForKey("cambio").
this -> thisElem
Ok I saw what I did wrong: I didn't link my CategoryCell and CurrencyCell classes with the Prototype Cell in the Storyboard.
CategoryCell and CurrencyCell are my classes that extend UITableViewCell in order to manage all cells of tho TableView.
Now I link these classes with storyboard properly and now it works fine!
Related
(Before you mark as duplicate you have to read the whole question and I am posting this cause I din't found the relevant and proper solution also need the solution in swift)
I have created one demo project and load and displayed name and area from array on custom cell.
I have noticed that after every 5th cell means 6th row is repeating with contents of 0th cell
for e.g.
the demo code is given below
class demoTableCell: UITableViewCell {
#IBOutlet var name : UILabel!
#IBOutlet var area : UILabel!
}
extension ViewController:UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.arrDemo.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
var cell : demoTableCell = demoTable.dequeueReusableCell(withIdentifier: cellIdentifier)! as! demoTableCell
cell.name.text = (arrDemo.object(at: indexPath.row) as! NSDictionary).value(forKey: "Name") as? String
cell.area.text = (arrDemo.object(at: indexPath.row) as! NSDictionary).value(forKey: "Area") as? String
if indexPath.row == 0{
cell.name.isHidden = true
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
As I hide the first label on 0th cell so I found that 6th row is also effected with implemented functionality of 0th cell. It means that also hide label1 of every 6th cell as I have attached the screenshot below so you can get the exact issue (This issue happened only if table view is scrollable)
As I have try to solve this issue from this link also but facing the same issue and cannot find the proper solution, and I am stuck here.
Cells are reused, you have to make sure that every UI element is set to a defined state.
You are using an if clause but there is no else case or a default value.
Simple solution:
Just replace
if indexPath.row == 0 {
cell.name.isHidden = true
}
with
cell.name.isHidden = indexPath.row == 0
this sets the hidden property always to a defined state.
And the usual dont's
Do not use NSDictionary in Swift.
Do not valueForKey unless you really need KVC (actually here you don't).
Remember - the cells are being reused.
You hide the cell, but you never explicitly unhide the cell
When you come to row 6, you are re-using the cell that was at row 0, and isHidden = true
All you need to do is extend your check, and hide the rows that you need to be hidden, and explicitly show the cells that you need to see. If you also have a moving banner that you add - you will also need to check to see if it's been loaded, and remove it if not required. Remember - it may not be row 6 - that's just how it works out with the current screensize
If you do have significant differences between the cells you want to use, you might be better using two different classes - and then you don't have to think about hiding labels
class demoTableCell: DemoTableCellNormalRow {
#IBOutlet var name : UILabel!
#IBOutlet var area : UILabel!
}
class demoTableCell: DemoTableCellFirstRow {
#IBOutlet var area : UILabel!
#IBOutlet var movingBannerView : LCBannerView!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
if row == 0 {
var cell : demoTableCell = demoTable.dequeueReusableCell(withIdentifier: cellIdentifier)! as! DemoTableCellFirstRow
cell.area.text = (arrDemo.object(at: indexPath.row) as! NSDictionary).value(forKey: "Area") as? String
// populate the bannerview which already exists, or create a new one
return cell
} else {
var cell : demoTableCell = demoTable.dequeueReusableCell(withIdentifier: cellIdentifier)! as! DemoTableCellNormalRow
cell.name.text = (arrDemo.object(at: indexPath.row) as! NSDictionary).value(forKey: "Name") as? String
cell.area.text = (arrDemo.object(at: indexPath.row) as! NSDictionary).value(forKey: "Area") as? String
return cell
}
}
Implement prepareForReuse in your cell class
override func prepareForReuse() {
name.isHidden = false
}
I am getting following errors:
1) Non-optional expression of type 'UITableViewCell' used in a check for optionals
2) Value of type 'UITableViewCell' has no member 'congigureCell'
Please
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell:UITableViewCell = countryList.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell // Error 1 happens here {
let text: String!
if inSearchMode {
text = filteredCountriesList[indexPath.row]
} else {
text = countriesList[indexPath.row]
}
cell.congigureCell(text: text) // Error 2 happens here
return cell
} else {
return UITableViewCell()
}
}
1) The ! mark at the end of
countryList.dequeueReusableCell(withIdentifier: "cell")!
uses force unwrap to make it non-optional, so you shouldn't check it inside if let, or even better way is to just remove ! mark
2) congigureCell probably the method of different class, not UITableViewCell. You should substitude UITableViewCell by this class to cast it
Make sure you have done following steps.
Add cell identifier in storyboard to your custom cell. i.e "cell"
Assign delegate and datasource of your YourTableview to YOURViewController.swift via storyboard or in code.
In YOURViewController.swift access cell using datasource of table
view as.
Add a custom class of sub class UITableViewCell and assign it to
tour cell in storyboard.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = countryList.dequeueReusableCell(withIdentifier: "cell") as! YOURTableViewCellClass {
let text: String!
if inSearchMode {
text = filteredCountriesList[indexPath.row]
} else {
text = countriesList[indexPath.row]
}
cell.congigureCell(text: text) // Error 2 happens here
return cell }
The ! mark is uses to force unwrap the optional value that can be nil. But "if let" and "guard let" has been check for optionals, so you don't need ! mark.
Just use
if let cell:UITableViewCell = countryList.dequeueReusableCell(withIdentifier: "cell") as UITableViewCell
cell in this line is 'UITableViewCell', but congigureCell is not a member of UITableViewCell.
If you want to use your own cell(like MyCell), you should convert it to MyCell.
let myCell = cell as! MyCell
1 .Instead of dequeueReusableCellWithIdentifier: use dequeueReusableCellWithIdentifier:forIndexPath: the later one never provides a nil value so you dont need to worry about the 'nil error'.
2.UItableViewCell dont have configure cell or congigureCell as in your case instead you have to create a custom tableViewCell and add function as configureCell() and then in this line
if let cell:UITableViewCell = countryList.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell
replace as UITableViewCell as as yourCustomTableViewCellClass
Hi i'm trying to add UITableview inside a UITableViewCell. I had connected the outer tableview cell to the view controller and the inner tableview(inside the cell) to the custom cell class and do the following code
//cellforrow of the outer tableview:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCellWithIdentifier("HistoryCell")! as! OrderHistoryTableViewCell
print(ordersArray[indexPath.row].valueForKey("items"))
cell.lbl_orderStatus.text = ordersArray[indexPath.row].valueForKey("status") as? String
cell.lbl_time.text = ordersArray[indexPath.row].valueForKey("timestamp") as? String
let vc = OrderHistoryTableViewCell() // Custom cell class
vc.tableview_reload(ordersArray[indexPath.row]as! NSMutableDictionary) //pass value to the tableview inside the cell
return cell
}
//code in the custom cell class
func tableview_reload(dict : NSMutableDictionary){
orderItemsarray.removeAllObjects()
let itemsDict = dict.valueForKey("items") as! NSMutableDictionary
for (_,value) in itemsDict
{
let tempDict = value as! NSMutableDictionary
orderItemsarray.addObject(tempDict)
}
self.tbl_Items.reloadData() // fatal error: unexpectedly found nil while unwrapping an optional value
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.orderItemsarray.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
var cell : UITableViewCell!
cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell");
cell.textLabel?.text = "test"
return cell
}
The data is passed and the func tableview_reload in the custom cell class is called. But when i try to reload the tableview in the custom cell class fatal error:
unexpectedly found nil while unwrapping a optional value occurs.
I had checked the outlet connection and it is connected to the custom cell class. Please advice
Instead of creating new instance using OrderHistoryTableViewCell() you need to use reused cell that you have created using dequeueReusableCellWithIdentifier, so remove line let vc = OrderHistoryTableViewCell() and call tableview_reload method on cell.
cell.tableview_reload(ordersArray[indexPath.row]as! NSMutableDictionary)
return cell
I'm trying to use Realm in my UITableViewController and I'm running into issues whenever I try to find the object at a row index if I cast the object to its class (forgive me if I'm using the wrong terminology, I'm still pretty new to Swift, Realm ans iOS dev!)...
I have a Site class which looks like this, and the database has a few thousand entries:
class Site: RLMObject {
var id: String = ""
var name: String = ""
}
In my table view controller, when I try to fetch a Site based on its index in the result set to load into a cell, if I try to cast it to a Site object it's always nil! If I let it be set using AnyObject, then I can see that the correct site at that index has indeed been found.
I'm guessing the .name call on AnyObject is only working because AnyObject responds to .name, but it helps me to see that the index is correct and that the site does exist...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let allSites = Site.allObjects()
var any: AnyObject = allSites.objectAtIndex(UInt(indexPath.row))
var site = allSites.objectAtIndex(UInt(indexPath.row)) as? Site
println("With AnyObject: \(any.name)")
println("With casting: \(site?.name)")
return cell
}
The result of the print statements above look like this (for example on a site which is named 'Addeboda'):
With AnyObject: Addeboda
With casting: Optional("")
Am I doing something wrong? I've googled around a bit, and all the tutorials and answers I can find along similar lines suggest that results.objectAtIndex(index) as Class should be the right approach.
No cast needed
It seems that casting to Site is not needed. This works fine:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let allSites = Site.allObjects()
let site: AnyObject! = allSites[UInt(indexPath.row)]
cell.textLabel!.text = site.name
println("Site is: \(site.id)")
return cell
}
Seems to be a bug with either Swift or Realm. I'm guessing one of them gets confused when downcasting AnyObject! to something.
Initializing a new instance with correct type
However, if you really need to use the Site model class you can initialize a new RLMObject from the result:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let allSites = Site.allObjects()
let site = Site(object: allSites[UInt(indexPath.row)])
cell.textLabel!.text = site.name
println("Site is: \(site.id)")
return cell
}
First try
It is unfortunate to hear that you are having issues with Realm and Swift. I am by no means a Swift pro, but it looks like you are casting site to an optional, and the result of using the optional cast operator site?.name is also an optional. Hence getting Optional("").
Can you try to see if you have any better luck with the following?
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let allSites = Site.allObjects()
if var site = allSites[UInt(indexPath.row)] as? Site {
println("Site is: \(site.name)")
} else {
println("it not castable to Site. It is: \(toString(allSites[UInt(indexPath.row)].dynamicType))")
}
return cell
}
Also, you can use yourObject.dynamicType to get a reference to the objects real class type.
Best of luck
Here is some code from the tableview sample project:
class TableViewController: UITableViewController {
var array = DemoObject.allObjects().sortedResultsUsingProperty("date", ascending: true)
...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as Cell
let object = array[UInt(indexPath.row)] as DemoObject
cell.textLabel?.text = object.title
cell.detailTextLabel?.text = object.date.description
return cell
}
}
You should be able to cast on the line you are storing that indexPath.row object
I've a table view that has a custom table view cell in it. My problem is that when I try and assign a value to a variable in the custom UITableViewCell I get the stated error. Now, I think its because the said variable is not initialised, but it got me completely stumped.
This is the custom table cell:
import Foundation
import UIKit
class LocationGeographyTableViewCell: UITableViewCell
{
//#IBOutlet var Map : MKMapView;
#IBOutlet var AddressLine1 : UILabel;
#IBOutlet var AddressLine2 : UILabel;
#IBOutlet var County : UILabel;
#IBOutlet var Postcode : UILabel;
#IBOutlet var Telephone : UILabel;
var location = VisitedLocation();
func Build(location:VisitedLocation) -> Void
{
self.location = location;
AddressLine1.text = "test";
}
}
My cell for row at index path is:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
{
var addressCell = tableView.dequeueReusableCellWithIdentifier("ContactDetail") as? LocationGeographyTableViewCell;
if !addressCell
{
addressCell = LocationGeographyTableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "ContactDetail");
}
addressCell!.Build(Location);
return addressCell;
}
As I say I'm completely baffled, the Build function calls the correct function in the UITableViewCell.
Any help will be gratefully appreciated.
Ta
I just made a simple project with your code, and I have a nil "AddressLine1" label, which causes the same error you have. I assume we have the same problem.
I solved it by adding the identifier "ContactDetail" to the prototype cell in my storyboard.
I also suggest that you change your code a little :
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// This newer API ensures that you always get a cell, so no need for optionals.
// Also note the "let" that is preferred since you don't plan on changing addressCell
let addressCell = tableView.dequeueReusableCellWithIdentifier("ContactDetail", forIndexPath: indexPath) as LocationGeographyTableViewCell;
//addressCell.Build(Location); // the cell is a view, views should not know about model objects
addressCell.AddressLine1 = Location.addressLine1
// ... same for all labels, or do all that in a "configureCell" function
return addressCell;
}
I finally solved it. Instead of using the storyboard to define the UITableViewCell (as I'd done in the parent view controller) I created a Xib with the content and wired that up to the cell's class, referenced it in the TableViewController and allocated it in the cell created method.
viewDidLoad()
{
var nib = UINib(nibName: "LocationTableViewCell", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "locationCell")
}
var cell:LocationGeographyTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("locationCell") as LocationGeographyTableViewCell
cell.AddressLine1.text = "teetetette";
return cell;