I have the following code for one of my tables.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "InventoryTableCell", for: indexPath as IndexPath) as! InventoryTableViewCell
let inventory : Inventory = fetchedResultsController.object(at: indexPath as IndexPath) as! Inventory
cell.inventoryItem = inventory
cell.drawCell() //uses passed inventoryItem to draw it's self accordingly.
return cell
}
I'm getting a runtime error on cell.inventoryItem = inventory
It says Thread 1: EXC_BAD_ACCESS (error: CoreData: error: Failed to call designated initializer on NSManagedObject class )
It didn't do this in swift 2 so not sure what the error is. I'll take a screenshot of my model and created class.
I have no idea how to fix the error since this worked before. What changed?
Looks like I solved my issue, when you define the items you have to give a context to it now or it won't work.
On code that I did not show here for my InventoryTableViewCell I had defined inventoryItem incorrectly.
Here is the correct way to do it.
//Set inventory item as an InventoryType within the correct context
var inventoryItem = Inventory(context: (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext)
Related
I am having issues getting Realm's result object to be accessed correctly using UITableView's cellForRowAt.
Here's the setup:
UITableViewController is divided into sections based on the Objects's category (a string defined in the object).
UITableViewController has a segue to a UIViewController which takes form input. That view controller writes to Realm and then makes a call back via delegation to refresh the table view data.
When that screen dismisses and returns to the UITableViewController, as I try to add the row via category, I am getting empty objects. However, when I use a for loop within cellForRowAt, I can access the data.
Here is what I'm running in this section:
func loadItems() {
itemsList = try! Realm().objects(Items.self).filter("list_id = \(list_id)").sorted(byKeyPath: "item_category")
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "itemListCell", for: indexPath)
let categoryName = categories.categories[indexPath.section]
let currItem = itemsList[indexPath.row]
if currItem.item_category == categoryName {
cell.textLabel!.text = currItem.item_name
}
return cell
}
It seems to be evaluating category correctly and stepping into that block, but the object's item_name and item_category is null. Here is a screenshot of the debugger inside the if statement:
Debugger Image
Is there something I need to change with how I'm using the object, pulling data, etc, to get the data correct in the object?
Found my answer here: UITableView with Multiple Sections using Realm and Swift
This is the change I made to cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "itemListCell", for: indexPath)
let currItem = itemsList.filter("item_category = '\(categories[indexPath.section])'")[indexPath.row]
cell.textLabel!.text = currItem.item_name
return cell
}
The issue I was having was I was constantly pulling the first position results object and not the first position within a section. I needed to narrow down to my section and then pull the first row.
I think it may have to do with your filters predicate. Try changing
itemsList = try! Realm().objects(Items.self).filter("list_id = \(list_id)").sorted(byKeyPath: "item_category"
to this
itemsList = try! Realm().objects(Items.self).filter("list_id = '\(list_id)'").sorted(byKeyPath: "item_category"
Add single quotations around '(list_id)'.
Realm Filtering
When I call the tableView function I get the error in the title. My question is why won't the function take the two arguments even though they are of the requested type? Again I'm a newbie so please forgive me if the answer is obvious.
class TableViewController: UITableViewController {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell") as! UITableViewCell
let name = Array(shopItems.keys) [indexPath.row]
let cost = Array(shopItems.values) [indexPath.row]
cell.textLabel?.text = name + String(cost)
return cell
}
}
When I call the function like this:
"TableViewController.tableView(shopTableView, IndexPath: NSIndexPath)" I get the error: "Argument labels '(_:, IndexPath:)' do not match any available overloads"
Try use
let name = shopItems.keys[indexPath.row]
Instead of
let name = Array(shopItems.keys) [indexPath.row]
Better don't use force wrap, when it's not nesesery.
Try change
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell") as! UITableViewCell
to
guard let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell") as? UITableViewCell else {
return UITableViewCell()
}
EDIT: As #Sh_Khan say replace
func tableView(_ tableView: UITableView, cellForRowAt indexPath: NSIndexPath) -> UITableViewCell {
to
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
There is an easy and quick way to figure out yourself the proper overload
Select the entire method and press ⌘/ to comment out the code.
On the top level of the class type cellForRow. The first item in the code completion list is the proper method.
Press Return to insert the correct method.
Comment in the wrong method by selecting it again and pressing ⌘/.
Copy and paste the body of the wrong method into the correct one.
Delete the rest of the wrong method.
I got this example of using custom cells in my Swift project :
let cell = tableView.dequeueReusableCell(withIdentifier: "LabelCell", for: indexPath)
as! HeadlineTableViewCell
But in my project, I actually have an array of Custom cells called mycells.
So I thought I could simply change it to :
let cell = tableView.dequeueReusableCell(withIdentifier: "LabelCell", for: indexPath)
as! type(of:allCells[indexPath.row])
But no. The compiler complains about this :
Cannot create a single-element tuple with an element label
Maybe this is just dumb but I cant get why it wont work. Can someone help me and clarify whats going on?
I use something similar in my apps, this is how I resolved this problem
extension UITableViewCell {
#objc func configure(_ data: AnyObject) {}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = info.sectionInfo[indexPath.section].data[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: data.identifier.rawValue, for: indexPath)
cell.configure(data as AnyObject)
return cell
}
class DefaultCell: UITableViewCell {
override func configure(_ data: AnyObject) {
guard let data = data as? MyDesiredClass
else {
return
}
// do smth
}
}
in this case you don't need to pass cell type directly, because any cell contains configure func where you can fill all fields
You issue is some kind of syntax mistake that is not visible in the code you provided.
But Use generic instead:
Define something like this:
protocol Reusable: UIView {
static var identifier: String { get }
}
extend it for UITableViewCell:
extension Reusable where Self: UITableViewCell {
static var identifier: String {
return String(describing: self)
}
}
Conform cell to it:
extension HeadlineTableViewCell: Reusable {}
add this extension to UITableView:
extension UITableView {
func dequeueReusableCell<T: UITableViewCell & Reusable>(type cellType: T.Type, for indexPath: IndexPath) -> T {
return dequeueReusableCell(withIdentifier: cellType.identifier, for: indexPath) as! T
}
}
and use it like this:
myTableView.dequeueReusableCell(type: HeadlineTableViewCell.self, for: indexPath)
This will dequeue and cast at the same time
I'm assuming that allCells is an array that contains a list of table view cells, but the cells are of different class types.
This line here that you're using can not be used the way that you're trying to use it.
type(of:allCells[indexPath.row])
That's why you're getting the error. This function returns the objects Metatype and you can't use that result in the way you're trying above. You should probably look into how optionals work too and how to unwrap them because the way you're trying to do this isn't going to work. This line you had here below would work fine, but unwrapping using type(of:) syntax is not going to work:
let cell = tableView.dequeueReusableCell(withIdentifier: "LabelCell", for: indexPath) as! HeadlineTableViewCell
Honestly, the entire architecture of using arrays to store tableViewCells is wrong, and I'm not even sure what you're trying to accomplish by doing that, but I can almost 100% say it's a very bad idea. Instead the array should store the data that the tableViewCell is going to display.
Honestly if I were you I would look up a tutorial on table views because I feel like there's a lot of misunderstanding here of how table views work which has led to the code that you wrote and the problems that you're having.
Check out this tutorial here. It should help you get a better understanding of how things work.
so im trying to populate the rows in my table with the information stored in array of "events". where events is an object that has location and title data. for some reason im getting a bad instruction error thread 1 at the line thats commented. Any idea why?
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {// this sets the title and subtitle to the Title and Location in the given EventPlan
let cell = tableView.dequeueReusableCell(withIdentifier: Storyboard.EventCellIdentifier, for: indexPath)
cell.textLabel?.text = eventsArray[indexPath.row].title // Error here
cell.detailTextLabel?.text = eventsArray[indexPath.row].location
return cell
}
in your number of rows for section data source call, make sure that you're returning eventsArray.count
I'm getting back to using CloudKit and in the project I'm revisiting, I have a query fetch performed and I am left with an array of CKRecords. I'm setting this array to be displayed via TableController. Anyways, I have this one line of code (which works)... but I am just unsure why I am setting the indexPath as NSIndexPath.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "dining") as! table1cell
let restaurant: CKRecord = categories[(indexPath as NSIndexPath).row]
cell.Name.text = restaurant.value(forKey: "Name") as? String
return cell
With my other non-CKRecord TableController projects, I know I don't have to set the indexPath to itself, in essence. What am I missing here?
The use of the cast to NSIndexPath is pointless. Simply change the line to:
let restaurant: CKRecord = categories[indexPath.row]
If indexPath is not stored in an explicitly typed var, you probably casted it to avoid a compiler message. If the compiler does not know indexPath is of type NSIndexPath, accessing the .row property would likely cause an error.
Where are you declaring/storing indexPath? What happens when you remove the as NSIndexPath cast?
Edit: re-reading your question, I believe the answer is:
"You are not storing indexPath as itself, you are casting whatever is stored in indexPath to be of type NSIndexPath"