从一个视图控制器切换到另一个视图控制器时出错
Posted
技术标签:
【中文标题】从一个视图控制器切换到另一个视图控制器时出错【英文标题】: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。仅仅连接代理和数据源是不够的。
【参考方案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 数组将重新初始化并且更改消失。新数组根本不会显示。相反,它会重置为原始大小以上是关于从一个视图控制器切换到另一个视图控制器时出错的主要内容,如果未能解决你的问题,请参考以下文章
在 UITabBarController 中,如何从一个视图控制器切换到另一个视图控制器并保留标签栏?