Метод Swift UITableView reloadData () неожиданно обнаружил ошибку nil - PullRequest
0 голосов
/ 06 августа 2020

Я пытаюсь быстро создать очень простое приложение со списком дел, и когда я вызываю метод reloadData в моем UITableView, я получаю эту ошибку: «Неожиданно обнаружен 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(). Это устраняет ошибку go, но ни один из элементов не добавляется в представление таблицы.

Не уверен, что это необходимо, но вот изображение раскадровки, чтобы вы могли видеть все экраны введите описание изображения здесь

Извините, если это действительно очевидная проблема. Как я уже сказал, я новичок в приложениях swift и iOS в целом.

Заранее спасибо!

1 Ответ

1 голос
/ 07 августа 2020

Похоже, вы назначаете класс ViewController как первому контроллеру (который содержит табличное представление), И второму контроллеру (с текстовым полем).

Это не сработает.

Добавьте этот класс в свой проект, назначьте его как настраиваемый класс контроллера представления «Новый элемент» и подключите @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?)

, мы получим ссылку на контроллер представления «Новый элемент», который вот-вот появится, и назначим ему «закрытие обратного вызова».

Когда мы набираем текст и нажимаем кнопку «Добавить» в следующем контроллере, он «перезвонит» первому контроллеру, передавая только что набранный текст. В этом мы обновим массив данных, перезагрузим таблицу и вернемся в стек навигации.

...