gpt4 book ai didi

ios - tableView.cellForRow(在 : indexPath) returns nil?

转载 作者:搜寻专家 更新时间:2023-10-31 22:58:53 24 4
gpt4 key购买 nike

我正在尝试使用 CoreData 实现一个 tableView。该表有四种排序方式。我在实现前三个方面没有问题,但第四个不同,因为它是一个有关系的实体。在我可以添加项目的第二个 View Controller 中,我添加了获取现有项目信息并将它们显示在相关单元格中的功能。

该应用程序有 2 个 View Controller ,一个用于 tableView,另一个用于添加/编辑 tableView 正在查看的项目。接下来是两个类:

import UIKit
import CoreData

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate{

@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var segment: UISegmentedControl!

var controller: NSFetchedResultsController<Item>!

override func viewDidLoad() {
super.viewDidLoad()



tableView.delegate = self
tableView.dataSource = self

//self.tableView.register(ItemCell.self, forCellReuseIdentifier: "ItemCell")

generateData()
attemptFetchRequest()
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

if let sections = controller.sections {

let sectionInfo = sections[section]
return sectionInfo.numberOfObjects
}

return 0
}

func numberOfSections(in tableView: UITableView) -> Int {

if let sections = controller.sections {

return sections.count
}
return 0
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! ItemCell

configureCell(cell: cell, indexPath: indexPath as NSIndexPath)
return cell

}

func configureCell (cell: ItemCell, indexPath: NSIndexPath) {
let item = controller.object(at: indexPath as IndexPath)
cell.configCell(item: item)
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

if let objs = controller.fetchedObjects , objs.count > 0 {
let item = objs[indexPath.row]

performSegue(withIdentifier: "ItemVC", sender: item)
}
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ItemVC" {
if let destination = segue.destination as? ItemVC {
if let item = sender as? Item {
destination.itemtoEdit = item
}
}
}
}

func attemptFetchRequest() {

let fetchrequest: NSFetchRequest = Item.fetchRequest()


let dateSort = NSSortDescriptor(key: "created", ascending: false)
let priceSort = NSSortDescriptor(key: "price", ascending: true)
let alphabetSort = NSSortDescriptor(key: "title", ascending: true)
let typeSort = NSSortDescriptor(key: "toItemType.type", ascending: true)

if segment.selectedSegmentIndex == 0 {
fetchrequest.sortDescriptors = [dateSort]
}else if segment.selectedSegmentIndex == 1 {
fetchrequest.sortDescriptors = [priceSort]
}else if segment.selectedSegmentIndex == 2 {
fetchrequest.sortDescriptors = [alphabetSort]
}else if segment.selectedSegmentIndex == 3{
fetchrequest.sortDescriptors = [typeSort]
}


let controller = NSFetchedResultsController(fetchRequest: fetchrequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
controller.delegate = self
self.controller = controller

do{
try controller.performFetch()
} catch {
let error = error as NSError
print("\(error)")
}
}

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch (type) {
case .insert:
if let indexPath = newIndexPath{
tableView.insertRows(at: [indexPath], with: .fade)
}
break
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
break
case .update:
if let indexPath = indexPath {
>>let cell = tableView.cellForRow(at: indexPath) as! ItemCell
configureCell(cell: cell, indexPath: indexPath as NSIndexPath)
}
break
case .move:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .fade)
}
if let indexPath = newIndexPath {
tableView.insertRows(at: [indexPath], with: .fade)
}
break
}
}

@IBAction func segmentChanged(_ sender: AnyObject) {
attemptFetchRequest()
tableView.reloadData()
}

func generateData() {

let item1 = Item(context: context)
item1.title = "Car of the cars"
item1.price = 100000
item1.details = "Nothing much to say, it's a crapy car, don't buy it"

let item2 = Item(context: context)
item2.title = "Rocket"
item2.price = 50000
item2.details = "It's not fast as the actual rocket, but still faster than a bicycle"

let item3 = Item(context: context)
item3.title = "bal bla bla"
item3.price = 50
item3.details = "The price talks!"

let item4 = Item(context: context)
item4.title = "Old is Gold"
item4.price = 60000000
item4.details = "It's old, but also considered as great inheritance"
}


}

和第二个 View Controller 的类:

import UIKit
import CoreData

class ItemVC: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

@IBOutlet weak var storesPicker: UIPickerView!
@IBOutlet weak var name : UITextField!
@IBOutlet weak var price : UITextField!
@IBOutlet weak var details : UITextField!
@IBOutlet weak var image: UIImageView!

var stores = [Store]()
var types = [ItemType]()

var itemtoEdit: Item?

var imagePicker: UIImagePickerController!

override func viewDidLoad() {
super.viewDidLoad()

if let topItem = self.navigationController?.navigationBar.topItem {

topItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.plain, target: nil, action: nil)
}

storesPicker.delegate = self
storesPicker.dataSource = self

imagePicker = UIImagePickerController()
imagePicker.delegate = self

generateData()

fetchRequest()

if itemtoEdit != nil {
loadData()
}

}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {

var returnValue = 0

switch component {
case 0:
returnValue = stores.count
case 1:
returnValue = types.count
default:
break
}

return returnValue
}

func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 2
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {

var returnValue : String!

switch component {
case 0:
returnValue = stores[row].name
case 1:
returnValue = types[row].type
default:
break
}

print(returnValue)
return returnValue
}

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
//update
}

func fetchRequest (){
let storefetch : NSFetchRequest<Store> = Store.fetchRequest()
let typefetch : NSFetchRequest<ItemType> = ItemType.fetchRequest()

do {
self.stores = try context.fetch(storefetch)
self.types = try context.fetch(typefetch)
self.storesPicker.reloadAllComponents()
} catch {
//print("fetch error")
}
}


@IBAction func saveItem(_ sender: AnyObject) {

var item : Item!

let pic = Image(context: context)
pic.image = image.image

if itemtoEdit == nil {
item = Item(context: context)
} else {
item = itemtoEdit
}

item.toImage = pic

if let title = name.text{
item.title = title
}

if let price = price.text {
item.price = (price as NSString).doubleValue
}

if let details = details.text {
item.details = details
}

item.toStore = stores[storesPicker.selectedRow(inComponent: 0)]

>>item.toItemType = types[storesPicker.selectedRow(inComponent: 1)]



ad.saveContext()

_ = navigationController?.popViewController(animated: true)
//dismiss(animated: true, completion: nil)
}

func loadData() {

if let item = itemtoEdit {

name.text = item.title
price.text = "\(item.price)"
details.text = item.details
image.image = item.toImage?.image as? UIImage


if let store = item.toStore {

var index = 0

repeat{


if store.name == stores[index].name {

storesPicker.selectRow(index, inComponent: 0, animated: false)
}

index += 1
} while(index < stores.count)
}

if let type = item.toItemType {

var index = 0

repeat{


if type.type! == types[index].type! {

storesPicker.selectRow(index, inComponent: 1, animated: false)
}

index += 1
} while(index < types.count)
}


}
}



@IBAction func deleteItem(_ sender: UIBarButtonItem) {
if itemtoEdit != nil {
context.delete(itemtoEdit!)
ad.saveContext()
}
_ = navigationController?.popViewController(animated: true)
}

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

if let img = info[UIImagePickerControllerOriginalImage] as? UIImage {
image.image = img
}
imagePicker.dismiss(animated: true, completion: nil)
}

@IBAction func setImg(_ sender: AnyObject) {

present(imagePicker, animated: true, completion: nil)

}

func generateData(){

let store1 = Store(context: context)
store1.name = "Karfour"
let store2 = Store(context: context)
store2.name = "خير زمان"
let store3 = Store(context: context)
store3.name = "BestBuy"
let store4 = Store(context: context)
store4.name = "Virgin"
let store5 = Store(context: context)
store5.name = "Max"

let type1 = ItemType(context: context)
type1.type = "eletronics"
let type2 = ItemType(context: context)
type2.type = "food"
let type3 = ItemType(context: context)
type3.type = "wears"
let type4 = ItemType(context: context)
type4.type = "books"
let type5 = ItemType(context: context)
type5.type = "weapons"

ad.saveContext()

}

}

我测试了这个错误,发现它从函数返回 nill :

let cell = tableView.cellForRow(at: indexPath) as! ItemCell

我非常确定导出并以正确的方式设置自定义类,所以我测试并注意到当我删除某行时,错误不再显示。我注意到的另一件事是,当我第一次在模拟器中运行该应用程序时,没有存储该应用程序的数据,即使使用之前导致问题的行,它也能完全正常工作,但在那之后,当我重新运行时问题显示的应用程序。 core data model core data model core data model core data model error我搜索了使 cellForRow 的返回值成为 will 的原因,但找不到任何有用的信息。我希望你能帮助我。提前致谢。

已解决使用 if let 展开“cell”,如下所示:

case .update:
if let indexPath = indexPath {
if let cell = tableView.cellForRow(at: indexPath) as? ItemCell {
configureCell(cell: cell, indexPath: (indexPath as NSIndexPath))
}
}
break

最佳答案

根据您的描述,我认为这是正在发生的事情:

您点击 ViewController 中的一个单元格,然后转到 ItemVC。保存项目时,将调用获取的结果 Controller 委托(delegate)方法。在该方法中,您使用 cellForRow(at:) 获取与已更新的 Item 对应的单元格。现在,该行必须可见(尽管在 ItemVC 的“后面”)——否则您还能如何点击它? - 所以 cellForRow(at:) 返回您需要配置的单元格。

这就是您的代码的工作方式,并且一切似乎都正常,直到您添加了更新关系的行:

item.toItemType = types[storesPicker.selectedRow(inComponent: 1)]

您说从ItemTypeItem 的关系是一对一的。假设您有一个与“食物”ItemType 相关的 itemA。在您的 ViewController 中,您点击不同项目的单元格 - itemB。在 ItemVC 中,您更新了 itemB 的属性并将其分配给“食物”ItemType。但是每个 ItemType 只能与一个 Item 相关。因此从 itemA 到“食物”ItemType 的关系被移除。这意味着 itemAtoItemType 值设置为 nil。所以有两个 Item 对象受到影响:

  • itemA 将其 toItemType 设置为 nil,并且
  • itemB 将其 toItemType 设置为“食物”ItemType

这两个更新都会被 FRC 观察到,并且会为每个更新调用委托(delegate)方法。在 itemB 的情况下,一切都像以前一样正常工作 - 它是被点击的单元格,因此它必须是可见的。但在 itemA 的情况下,它可能会或可能不会可见。如果它不可见,cellForRow(at:) 返回 nil,您的原始代码就会崩溃。通过在 cell 上使用可选绑定(bind),您可以避免崩溃。相应的行没有更新,但没关系:无论如何它都不可见,当它在屏幕上滚动时,它将被正确配置为正常处理的一部分。

关键在于,因为从 ItemTypeItem 的关系是一对一的,所以每当您在 ItemVC 。我认为您可能希望建立这种关系。然后将 itemB 添加到“食物”ItemType 将不会对 itemA 产生任何影响:它们都可能与其相关。

关于ios - tableView.cellForRow(在 : indexPath) returns nil?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39990672/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com