【问题标题】:Error while switching from one view controller to another从一个视图控制器切换到另一个视图控制器时出错
【发布时间】:2016-07-03 13:20:17
【问题描述】:

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

我为两个视图控制器制作了单独的 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()
}

}

我的第二个视图控制器的代码,它具有文本字段和一个用于将文本插入我的数组(项目)的按钮

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.
   } 

}

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

【问题讨论】:

  • 你需要实现UITableViewDataSource和UITableViewDelegate所需的功能
  • @Roee84 我的 TableView 已经连接到 DataSource 和 Delegate。我在情节提要中做到了。你这是什么意思?你能解释一下吗?我还设置了单元格和所有内容,但此处未显示代码
  • 您的tableview 出口似乎是nil。你把它连接到故事板了吗?
  • @vacawama 是的。它连接到数据源和委托。实际上,我注意到的一件有趣的事情是,对于我的第二个视图控制器文件,如果我说“class TextScreen : UIViewController”,我成功地转换到了第二个控制器。但是,如果父类是 MainScreen (第一个视图控制器),我会收到此错误。为什么会这样?
  • 这个属性@IBOutlet weak var tableview: UITableView!也必须连接到tableView。仅仅连接代理和数据源是不够的。

标签: ios swift uitableview uiviewcontroller uitextfield


【解决方案1】:

您想要实现的目标应该使用委托方法处理,而不是继承您的 MainScreen。

协议规定了一个类的结构。如果协议中定义了一个函数。实现协议的类应该具有该功能。这就是为什么如果你从 MainScreen 中引用委托,比如委托 = 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 的测试目的。正确的方法是使用 Core Data。 google上有很多例子。

【讨论】:

  • 非常感谢!但是,这听起来可能很愚蠢,但我没有阅读协议,这就是为什么我不理解您编写的大部分代码的原因。你能带我过去吗?另外,如果我理解正确,我会在 MainScreen 文件中创建协议吗?请帮忙。另外,TextScreen 应该从 MainScreen 导入,对吗?不是 UIViewController?
  • 我不明白您的代码中 prepareForSegue 的工作原理和原因。你能解释一下吗?
  • 我已经更新了我的答案,您能否重新检查一下,如果您不明白,请告诉我!可以在 1 周后服用药丸;)
  • 非常感谢,空虚!我将通过它并玩弄它,如果我不明白它会告诉你。非常感谢!祝福你! :)
  • 我明白你做了什么。现在,如果我在字段中输入文本,它确实会附加到我的数组中。但是,如果我点击返回并转到我的 MainScreen,我的 items 数组将重新初始化并且更改消失。新数组根本不会显示。相反,它会重置为原始大小
猜你喜欢
  • 2021-12-31
  • 1970-01-01
  • 1970-01-01
  • 2015-03-23
  • 1970-01-01
  • 2013-07-25
  • 2013-03-12
  • 2015-10-04
  • 1970-01-01
相关资源
最近更新 更多