从一个视图控制器切换到另一个时,出错

时间:2016-07-03 13:20:18

标签: ios swift uitableview uiviewcontroller uitextfield

我有两个ViewControllers。一个有一个TableView和一个Add Button,它将我们带到第二个ViewController,它有一个文本字段和一个动作按钮。用户应该在具有文本字段的第二个屏幕上的tableview中插入文本。

我为两个视图控制器制作了单独的swift文件。对于第二个视图,我正在导入我的第一个视图的类。但是,当我尝试点击添加按钮去第二个视图控制器时,我得到一个可选的包装错误。这是我得到的错误:致命错误: unexpectedly found nil while unwrapping an Optional value 。在调试模式下,它突出显示以下代码行:

    tableview.setEditing(true, animated: true)

我的第一个具有tableview的视图控制器的代码:

class MainScreen : UIViewController, UITableViewDataSource, UITableViewDelegate {


var items  = ["Apple", "Fish", "Dates", "Cereal", "Ice cream", "Lamb", "Potatoes", "Chicken", "Bread"]


@IBOutlet weak var tableview: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view, typically from a nib.
    tableview.setEditing(true, animated: true)

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1
}

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

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let iD = "normal"
    let cell = tableView.dequeueReusableCellWithIdentifier(iD, forIndexPath: indexPath)
    cell.textLabel?.text = items[indexPath.row]
    return cell
}


func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle {
    return UITableViewCellEditingStyle.None
}



func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
    if (sourceIndexPath.row != destinationIndexPath.row){
        let temp = items[sourceIndexPath.row]
        items.removeAtIndex(sourceIndexPath.row)
        items.insert(temp, atIndex: destinationIndexPath.row)

    }
    tableView.reloadData()
}

}

我的第二个视图控制器的代码,它包含textfield和一个用于将文本插入到我的数组中的按钮(项目)

class TextScreen : MainScreen {


    @IBOutlet weak var keyboard: UITextField!

    @IBAction func insert(sender: AnyObject) {

        items.append(keyboard.text!)

    }


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically   from a nib.
    }

   override func didReceiveMemoryWarning() {
       super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
   } 

}

我不知道自己做错了什么。我还附上了截图。

1 个答案:

答案 0 :(得分:0)

您尝试实现的目标应使用委托方法处理,而不是将MainScreen子类化。

协议规定了类的结构。如果在协议中定义了函数。实现协议的类应该具有该功能。这就是为什么如果你引用来自MainScreen的委托就像delegate = self那样,持有委托的类可以触发协议中规定的任何函数。这会将函数中的数据从TextScreen传递到MainScreen中实现的函数

您想要将字符串附加到数组,您需要一个发送字符串(或处理它)的函数。礼仪也包括发件人。

创建协议:

protocol TextScreenDelegate {
    func sendTextToDelegate(sender: TextScreen, text: String)
}

现在,您应该将一个委托放在TextScreen视图中作为该类的属性:

此委托将在想要使用数据的类和保存数据的类之间创建链接。当执行TextScreen中的协议的方法(delegate.sendTextToDelegate())时,它将转向引用它的任何视图,例如prepareforSegue中的MainScreen(参见下文)

var delegate : TextScreenDelegate? 

现在让MainScreen符合您新创建的协议,如下所示:

现在我们需要确保MainScreen符合此协议。就像你使用UITableView等一样,你需要添加自己的协议。请记住,如果您不使用协议实现,TableView将无法使用我们的TextScreen!

class MainScreen : UIViewController, UITableViewDataSource, UITableViewDelegate, TextScreenDelegate

它会说它不符合,因为您缺少上面创建的委托方法。实施此操作并执行您想要执行的操作:

现在您已将协议添加到MainScreen,您应该创建自己在协议中定义的功能。其中的每个功能都需要添加到您的MainScreen中,如下所示:

func sendTextToDelegate(sender : TextScreen, text : String) {
    self.items.append(text)
}

为了能够让TextView知道您想要将数据传播到该委托,您需要引用它。让你的prepareForSegue看起来像这样:

这是我们在创建TextScreen和MainScreen之间的链接时所讨论的内容。 MainScreen(实现协议TextScreenDelegate)用于链接(请参阅vc.delegate = self)

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "YourIdentifier" {
        if let vc = segue.destinationViewController as? TextScreen {
            vc.delegate = self
        }
    }
}
剩下的就是现在调用TextScreen中的函数!当你执行你的行动时,让代表知道! 现在,在TextScreen中,您只需调用委托方法即可。使用您的代码:

@IBAction func insert(sender: AnyObject) {

    // Always check if exists!
    guard let text = self.keyboard.text
        else {
            print("error - no text in keyboard")
            return
    }
    delegate?.sendTextToDelegate(sender: self, text: text)
}

确保TextScreen再次是UIView而不是MainScreen的子类:

TextScreen : UIView

这是一个独立的观点;)

这似乎是一种更清洁的方法来处理你想做的事情。

- 编辑2 -

测试委托方法有一种肮脏的方法。在AppDelegate中创建一个数组并附加到该数组。

在AppDelegate.swift

var items : [String] = ["Apple", "Fish", "Dates", "Cereal", "Ice cream", "Lamb", "Potatoes", "Chicken", "Bread"]

现在在你的MainScreen.swift

将您的商品更改为:

var items : [String] = []

并在你的viewDidLoad()

let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
self.items = appDelegate.items
self.tableView.reloadData()

并在你TextScreen.swift

let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.items.append('your item')

这会将它们保存在AppDelegate的数组中。但是,这仅建议用于tableView的测试目的。正确的方法是使用核心数据。其中有很多关于谷歌的例子。