Navigation Controller中如何保留第二个ViewController的输入值?
嘿,这个问题我之前也碰到过!核心原因其实是你每次跳转到SecondVC的时候,都用instantiateViewController创建了一个全新的实例——之前输入的内容都在旧实例里,新实例当然是空的啦。下面给你几个靠谱的解决方案,按需选就行:
这是最直接的办法,在FirstVC里保存一个SecondVC的引用,只在第一次需要的时候实例化它,之后跳转都用这个已存在的实例。
代码示例(在FirstVC中):
// 声明可选属性,用来缓存SecondVC实例 private var secondVC: SecondVC? // 跳转触发方法(比如按钮点击) @IBAction func navigateToSecondVC(_ sender: UIButton) { // 只有当实例不存在时才创建 if secondVC == nil { let storyboard = UIStoryboard(name: "Main", bundle: nil) secondVC = storyboard.instantiateViewController(withIdentifier: "SecondVC") as! SecondVC } navigationController?.show(secondVC!, sender: self) }
这样每次跳转都是同一个SecondVC实例,里面的TextField内容自然会保留。注意:如果你的SecondVC需要根据FirstVC的状态动态更新内容,记得在跳转前做相应的重置或更新操作。
如果你的项目后续可能有更多页面需要共享数据,或者不想耦合VC实例,用ViewModel是更优雅的选择。创建一个独立的ViewModel类来保存输入数据,让两个VC都绑定到这个ViewModel的属性上。
步骤:
- 先创建ViewModel类:
class FormViewModel { // 对应四个TextField的属性 var field1: String = "" var field2: String = "" var field3: String = "" var field4: String = "" }
- 在
FirstVC中初始化ViewModel,并传递给SecondVC:
// FirstVC中声明ViewModel实例 private let formViewModel = FormViewModel() // 跳转方法 @IBAction func navigateToSecondVC(_ sender: UIButton) { let storyboard = UIStoryboard(name: "Main", bundle: nil) let destinationVC = storyboard.instantiateViewController(withIdentifier: "SecondVC") as! SecondVC // 把ViewModel传给SecondVC destinationVC.viewModel = formViewModel navigationController?.show(destinationVC, sender: self) }
- 在
SecondVC中绑定TextField和ViewModel:
// SecondVC中声明ViewModel属性 var viewModel: FormViewModel! // 监听TextField编辑事件(可以给每个TextField设置tag区分) @IBAction func textFieldDidChange(_ sender: UITextField) { switch sender.tag { case 1: viewModel.field1 = sender.text ?? "" case 2: viewModel.field2 = sender.text ?? "" case 3: viewModel.field3 = sender.text ?? "" case 4: viewModel.field4 = sender.text ?? "" default: break } } // 在页面即将显示时,把ViewModel的数据同步到TextField override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) textField1.text = viewModel.field1 textField2.text = viewModel.field2 textField3.text = viewModel.field3 textField4.text = viewModel.field4 }
这个方案的好处是数据和VC分离,就算销毁了SecondVC实例,数据依然保存在ViewModel里,下次创建新的SecondVC时,只要传递ViewModel就能恢复之前的输入内容。而且如果以后有其他页面需要用到这些数据,直接复用ViewModel就行。
如果不想复用SecondVC实例,也不想用ViewModel,可以在SecondVC返回的时候把输入数据传递给FirstVC保存,下次跳转时再把数据传给新的SecondVC。
步骤:
- 在
SecondVC中定义闭包用来传递数据:
// 定义闭包,用于返回时传递TextField数据 var onDataSaved: ((String, String, String, String) -> Void)? // 返回按钮的点击方法 @IBAction func goBackToFirstVC(_ sender: UIButton) { // 把当前TextField的值通过闭包传递回去 onDataSaved?(textField1.text ?? "", textField2.text ?? "", textField3.text ?? "", textField4.text ?? "") navigationController?.popViewController(animated: true) }
- 在
FirstVC中保存数据,并在跳转时传递给新的SecondVC:
// FirstVC中保存SecondVC的输入数据 private var secondVCInputData: (field1: String, field2: String, field3: String, field4: String) = ("", "", "", "") // 跳转方法 @IBAction func navigateToSecondVC(_ sender: UIButton) { let storyboard = UIStoryboard(name: "Main", bundle: nil) let destinationVC = storyboard.instantiateViewController(withIdentifier: "SecondVC") as! SecondVC // 把之前保存的数据赋值给新的SecondVC的TextField destinationVC.textField1.text = secondVCInputData.field1 destinationVC.textField2.text = secondVCInputData.field2 destinationVC.textField3.text = secondVCInputData.field3 destinationVC.textField4.text = secondVCInputData.field4 // 设置闭包,接收返回的数据 destinationVC.onDataSaved = { [weak self] field1, field2, field3, field4 in self?.secondVCInputData = (field1, field2, field3, field4) } navigationController?.show(destinationVC, sender: self) }
这个方案适合简单场景,但如果TextField数量多或者后续有扩展,代码会变得繁琐,不如ViewModel优雅。
内容的提问来源于stack exchange,提问作者atalayasa

