2

我已经学习 Swift 和 Xcode 大约 3 周了,我正在做一个 Todo 应用程序只是为了练习。但是,当我尝试将新项目添加到我的待办事项列表时,发生错误说“在展开可选值时意外发现 nil”。

我有由 Main.storyboard 创建的主屏幕和主屏幕上的添加按钮。当单击该按钮时,它会转到我通过 Eureka 框架创建的新 ViewController,用户可以在该表单中输入一些信息,如标题、描述、类别并将它们传递回主屏幕。我使用委托协议来传递数据,当我单击“保存项目”按钮时,应用程序崩溃了,它说

“致命错误:在展开可选值时意外发现 nil”。

代码如下:

import Foundation
import Eureka
protocol CanReceive {

    func dataReceived(data: ToDo)

}
class AddItemViewController : FormViewController {

    var delegate : CanReceive?

    var todoItem : ToDo?

    static let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "MMM d yyyy, h:mm a"
        return formatter
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        form +++ Section()
            <<< TextRow(){
                $0.title = "Title"
                $0.placeholder = "Enter text here"
                $0.onChange { [unowned self] row in
                    self.todoItem?.title = row.value
                }
            }
            <<< TextRow(){
                $0.title = "Description"
                $0.placeholder = "Give some description"
                $0.onChange { [unowned self] row in
                    self.todoItem?.description = row.value
                }
            }

            <<< AlertRow<String>() {
                $0.title = "Category"
                $0.selectorTitle = "Select the category"

                $0.options = ["Personal ", "Home ", "Work ", "Play ", "Health ‍♀️" , "Other"]
                $0.onChange { [unowned self] row in
                    self.todoItem?.category = row.value
                }
                }

            +++ Section(){ section in
                section.header = {
                    var header = HeaderFooterView<UIView>(.callback({
                        let button = UIButton(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
                        button.backgroundColor = .darkGray
                        button.setTitle("Save Item", for: .normal)
                        button.addTarget(self, action: #selector(self.buttonAction), for: .touchUpInside)

                        return button
                    }))
                    header.height = { 50 }
                    return header
                }()
        }
    }

    @objc func buttonAction(sender: UIButton!) {
        delegate?.dataReceived(data: todoItem!)
        self.dismiss(animated: true, completion: nil)

        print("Button tapped")
    }
}

Todo 类型定义为:

struct ToDo {

    var title: String?
    var description: String?
    var category : String?

}

这是控制 main.storyboard 的 ViewController :

import UIKit

let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)




class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, CanReceive {

    var todoList = [ToDo]()
    let cellId = "CellId"
    let test = ["Row1", "Row2", "Row3"]
    let imageName = ["anchor", "arrow-down", "aperture"]
    @IBOutlet weak var ImageTop: UIImageView!
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.register(UINib(nibName: "CustomTableViewCell", bundle: nil), forCellReuseIdentifier: cellId)
        configureTableView()
        ImageTop.image = UIImage(named: "todo-background")


    }

    //data source method

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return todoList.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! CustomTableViewCell
        cell.categoryLabel?.text = todoList[indexPath.row].category
        cell.descriptionLabel.text = todoList[indexPath.row].description
        var imageCategory = ""
        switch todoList[indexPath.row].category {
        case "Personal ":
            imageCategory = "Personal"
        case "Home ":
            imageCategory = "Home"
        case "Work ":
            imageCategory = "Work"
        case "Play ":
            imageCategory = "Play"
        case "Health ‍♀️":
            imageCategory = "Health"
        case "Other":
            imageCategory = "Other"
        default:
            break
        }
        cell.imageCategory?.image = UIImage(named: imageCategory)
        return cell
    }

    func configureTableView() {
        self.tableView.rowHeight = 80
        self.tableView.separatorStyle = .none

    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("Did select row at index \(indexPath.row)")
        print("Row height is: \(tableView.rowHeight)")
    }
    @IBAction func addButtonPressed(_ sender: RoundButton) {
        let addItemViewController = AddItemViewController()
        addItemViewController.delegate = self
        self.present(addItemViewController, animated:true, completion:nil)
    }

 //Delegate 
    func dataReceived(data: ToDo) {
        todoList.append(data)
        return
    }
}

感谢您抽出宝贵时间,如果我的问题如此愚蠢,我们深表歉意。

4

2 回答 2

2

发生错误是因为在AddItemViewController属性todoItem中已声明但未初始化。

我建议对三个属性使用临时变量,并ToDo在按下按钮时创建一个实例

class AddItemViewController : FormViewController {

    var delegate : CanReceive?

    var title, description, category : String?

    static let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "MMM d yyyy, h:mm a"
        return formatter
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        form +++ Section()
            <<< TextRow(){
                $0.title = "Title"
                $0.placeholder = "Enter text here"
                $0.onChange { [unowned self] row in
                    self.title = row.value
                }
            }
            <<< TextRow(){
                $0.title = "Description"
                $0.placeholder = "Give some description"
                $0.onChange { [unowned self] row in
                    self.description = row.value
                }
            }

            <<< AlertRow<String>() {
                $0.title = "Category"
                $0.selectorTitle = "Select the category"

                $0.options = ["Personal ", "Home ", "Work ", "Play ", "Health ‍♀️" , "Other"]
                $0.onChange { [unowned self] row in
                    self.category = row.value
                }
                }

            +++ Section(){ section in
                section.header = {
                    var header = HeaderFooterView<UIView>(.callback({
                        let button = UIButton(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
                        button.backgroundColor = .darkGray
                        button.setTitle("Save Item", for: .normal)
                        button.addTarget(self, action: #selector(self.buttonAction), for: .touchUpInside)

                        return button
                    }))
                    header.height = { 50 }
                    return header
                }()
        }
    }

    @objc func buttonAction(sender: UIButton) { // no implicit unwrapped optional
        let todoItem = ToDo(title: title, description: description, category : category)
        delegate?.dataReceived(data: todoItem)
        self.dismiss(animated: true, completion: nil)

        print("Button tapped")
    }
}
于 2019-04-18T08:51:43.437 回答
1

我认为这一行delegate?.dataReceived(data: todoItem!)导致了您的错误,因为您在未初始化时隐式打开了 todoItem

于 2019-04-18T09:04:03.520 回答