【问题标题】:Swift UITableView reloadData() method unexpectedly found nil errorSwift UITableView reloadData() 方法意外发现 nil 错误
【发布时间】:2020-11-27 04:27:32
【问题描述】:

我正在尝试快速创建一个非常简单的待办事项列表应用程序,当我在 UITableView 上调用 reloadData 方法时,我收到此错误:“在隐式展开可选值时意外发现 nil”。当用户在与 tableView 不同的视图控制器上的文本字段中键入内容后单击添加按钮时,我正在调用此方法。他们键入的内容应该被添加到表格视图中,但它没有,我只是得到一个错误。

我在网上查找了遇到类似问题的人,但我不知道如何将它们实现到我的代码中,或者我不理解它们,因为我对 swift 非常陌生。我还尝试将文本字段与表格视图放在同一个视图控制器上并解决了问题,所以我猜它与此有关。

我的所有代码都在 ViewController.swift 中。这里是:

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var editButton: UIBarButtonItem!
    @IBOutlet weak var textField: UITextField!
    
    var tableViewData = ["Apple", "Banana", "Orange", "Peach", "Pear"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    // MARK: Tableview methods
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tableViewData.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = tableViewData[indexPath.row]
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // print(tableViewData[indexPath.row])
    }
    
    // Allows reordering of cells
    func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
        return true
    }
    
    // Handles reordering of cells
    func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        let item = tableViewData[sourceIndexPath.row]
        
        tableViewData.remove(at: sourceIndexPath.row)
        tableViewData.insert(item, at: destinationIndexPath.row)
    }
    
    // Allow the user to delete cells
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == UITableViewCell.EditingStyle.delete {
            tableViewData.remove(at: indexPath.row)
            tableView.reloadData()
        }
    }
    
    // MARK: IBActions
    
    @IBAction func edit(_ sender: Any) {
        tableView.isEditing = tableView.isEditing
        
        switch tableView.isEditing {
        case true:
            editButton.title = "Done"
        case false:
            editButton.title = "Edit"
        }
    }
    
    @IBAction func add(_ sender: Any) {
        let item: String = textField.text!
        tableViewData.append(item)
        textField.text = ""
        tableView.reloadData() // <------ **This line gives me the error**
    }
    
}

另外,我尝试了可选链接,这给我写了一个错误,tableView?.reloadData()。它使错误消失,但没有任何项目被添加到表格视图中。

不确定是否有必要,但这是故事板的图像,因此您可以看到所有屏幕

抱歉,如果这是一个非常明显的问题。就像我说的,我对 swift 和 iOS 应用程序很陌生。

提前致谢!

【问题讨论】:

  • 确保 tableView IBOutlet 已连接到情节提要。
  • 请务必检查情节提要中的表格视图是否正确连接到您的视图控制器类上的 tableView IBOutlet。
  • 实现方法 numberOfSection ... 并返回 1
  • @jawadAli 没有修复它。
  • @Aaron 它似乎连接正确。我右键单击表格视图并在引用插座中看到了连接

标签: ios swift uitableview uitextfield reloaddata


【解决方案1】:

看起来您正在将 ViewController 类分配给您的第一个控制器(包含表格视图)AND 到您的第二个控制器(带有文本字段) .

那是行不通的。

将该类添加到您的项目中,将其分配为“新项目”视图控制器的自定义类,并连接@IBOutlet@IBAction

class NewItemViewController: UIViewController {

    // callback closure to tell the VC holding the table view
    //  that the Add button was tapped, and to
    //  "send back" the new text
    var callback: ((String) -> ())?
    
    @IBOutlet weak var textField: UITextField!

    @IBAction func add(_ sender: Any) {
        let item: String = textField.text!
        callback?(item)
        textField.text = ""
    }
    
}

接下来,将您的 ViewController 类更改为以下内容:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var editButton: UIBarButtonItem!
    
    var tableViewData = ["Apple", "Banana", "Orange", "Peach", "Pear"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // if you're not already seeing "Apple", "Banana", "Orange", "Peach", "Pear"
        // add these two lines
        //tableView.dataSource = self
        //tableView.delegate = self
    }
    
    // MARK: Tableview methods
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tableViewData.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = tableViewData[indexPath.row]
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // print(tableViewData[indexPath.row])
    }
    
    // Allows reordering of cells
    func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
        return true
    }
    
    // Handles reordering of cells
    func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        let item = tableViewData[sourceIndexPath.row]
        
        tableViewData.remove(at: sourceIndexPath.row)
        tableViewData.insert(item, at: destinationIndexPath.row)
    }
    
    // Allow the user to delete cells
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == UITableViewCell.EditingStyle.delete {
            tableViewData.remove(at: indexPath.row)
            tableView.reloadData()
        }
    }
    
    // MARK: IBActions
    
    @IBAction func edit(_ sender: Any) {
        tableView.isEditing = !tableView.isEditing
        
        switch tableView.isEditing {
        case true:
            editButton.title = "Done"
        case false:
            editButton.title = "Edit"
        }
    }

    // when "New Item" button is tapped, it will segue to
    // NewItemViewController... set the callback closure here
    
    // prepare for segue is called when you have created a segue to another view controller
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        
        // error checking is always a good idea
        //  this properly unwraps the destination controller and confirms it's
        //  an instance of NewItemViewController
        if let vc = segue.destination as? NewItemViewController {
            // callback is a property we added to NewItemViewController
            //  we declared it to return a String
            vc.callback = { item in
                self.tableViewData.append(item)
                self.tableView.reloadData()
                self.navigationController?.popViewController(animated: true)
            }
        }
    }
    
}

当您点击“添加项目”按钮时,我们假设您已将其连接到“新项目”视图控制器。通过实施:

override func prepare(for segue: UIStoryboardSegue, sender: Any?)

我们将获得一个对即将出现的“New Item”视图控制器的引用,并为其分配一个“回调闭包”。

当我们输入一些文本并点击下一个控制器中的“添加”按钮时,它会“回调”到第一个控制器,传递新输入的文本。 是我们更新数据数组、重新加载表格并弹回导航堆栈的地方。

【讨论】:

  • 非常感谢您的回答!我只是想了解代码中发生了什么。我对闭包做了一些研究,所以我得到了更多,但我仍然需要更多解释。如果我错了,请纠正我,但是我们是否创建了一个在单击添加按钮时被调用的闭包,并且我们正在将项目传递给闭包?然后在 ViewController.swift 中点击“添加项目”之前,我们存储对 NewItemViewController 的引用,并以“项目”作为参数创建闭包,这就是我们调用闭包时执行的代码?跨度>
  • 由于评论空间有限,我还有其他问题无法提出。如果可以的话,如果您能回答他们,我将不胜感激(这将有很大帮助)。他们在这张图片的代码中被注释:imgur.com/a/F4aKPWS
  • @Zemelware - 我编辑了我的答案并在代码中添加了一些额外的 cmets 来尝试回答您的问题。听起来您对正在发生的事情有了一个大致的了解。以这种方式使用闭包是控制器之间通信的一种非常常见的方式。您也可以使用 protocol / delegate 模式,但 Swift 似乎更喜欢闭包。无论如何,差别很小,但随着您继续学习,您很快就会遇到协议和委托。
  • 谢谢,这真的很有帮助!我很感激!另外,UITableViewDataSource 和 UITableViewDelegate 不是已经协议了吗?
  • 嗨@DonMag,我想知道你能帮助解决与 ARkit/Scenekit 相关的问题吗?最好的问候。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多