Segue无法设置目标视图控制器属性,返回主VC时变量重置问题求助
你猜的完全正确!问题出在Show segue的工作机制上:当你用Show segue从二级控制器跳回主控制器时,系统并不是回到你原来的那个ViewController实例,而是新建了一个全新的ViewController对象。你在prepare(for:)里给vc.myDate赋值,其实是给这个新实例的变量赋值,而原来显示在屏幕上的主控制器根本没收到这个值,所以自然看不到效果。
下面给你几种靠谱的实现方式,按推荐程度排序:
方法1:使用Unwind Segue(最适配Storyboard的方式)
Unwind Segue是专门用来从子控制器回到父控制器(或之前的控制器)的机制,它会找到导航栈里已存在的目标控制器实例,不会新建。
步骤1:在主视图控制器中添加Unwind方法
在ViewController里添加一个带@IBAction的unwind方法,用来接收从二级控制器传回来的值:
class ViewController: UIViewController { var myDate = Date() override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) updateDateLabel(myDate) } // 添加这个unwind方法 @IBAction func unwindToMain(segue: UIStoryboardSegue) { // 从segue的source获取二级控制器实例 guard let calendarVC = segue.source as? CalendarViewController, let selectedDate = calendarVC.selectedDate else { return } // 更新当前主控制器的变量 self.myDate = selectedDate updateDateLabel(myDate) } // 你的updateDateLabel方法 func updateDateLabel(_ date: Date) { // ... 实现代码 } }
步骤2:在Storyboard中配置Unwind Segue
- 打开Storyboard,选中二级
CalendarViewController的视图(或者你用来触发返回的控件,比如日历的单元格)。 - 按住
Ctrl键,把控件拖到二级控制器顶部的Exit图标(就是那个绿色的箭头)上。 - 在弹出的菜单中选择你刚才创建的
unwindToMain:方法。 - 选中这个新建的Unwind Segue,在属性面板里设置它的
identifier为dateSelected(和你之前的标识保持一致也可以)。
步骤3:二级控制器中无需修改prepare方法
现在你不需要在CalendarViewController的prepare(for:)里做任何赋值操作,只要确保selectedDate在用户选择日期时被正确设置就行。当触发Unwind Segue时,系统会自动调用主控制器的unwind方法,你在那里完成变量更新。
方法2:使用闭包回调(代码更灵活)
如果不想依赖Storyboard的Segue配置,可以用闭包把选中的日期从二级控制器回调给主控制器,然后手动返回(pop/dismiss)。
步骤1:在二级控制器中定义闭包属性
class CalendarViewController: UIViewController { var selectedDate: Date? // 定义一个闭包,用来传递选中的日期 var onDateSelected: ((Date) -> Void)? func calendar(_ calendar: Any, didSelectDate date: Date, ...) { selectedDate = date // 调用闭包把日期传回去 onDateSelected?(date) // 手动返回主控制器(如果是导航栈push进来的用pop) navigationController?.popViewController(animated: true) // 如果是modal present进来的用下面这句: // dismiss(animated: true) } // 不需要prepare方法了 }
步骤2:在主控制器跳转时设置闭包
在主控制器的prepare(for:)里,给二级控制器的闭包赋值:
class ViewController: UIViewController { var myDate = Date() override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showCalendar" { // 这是跳转到二级控制器的segue标识 let calendarVC = segue.destination as! CalendarViewController // 设置闭包,接收返回的日期 calendarVC.onDateSelected = { [weak self] selectedDate in guard let self = self else { return } self.myDate = selectedDate self.updateDateLabel(selectedDate) } } } // ... 其他代码 }
方法3:使用代理模式(传统iOS传值方式)
如果你习惯用设计模式,代理也是个不错的选择,原理和闭包类似,只是用协议规范传值逻辑。
步骤1:定义代理协议
protocol CalendarViewControllerDelegate: AnyObject { func calendarViewController(_ vc: CalendarViewController, didSelectDate date: Date) }
步骤2:二级控制器添加代理属性
class CalendarViewController: UIViewController { weak var delegate: CalendarViewControllerDelegate? var selectedDate: Date? func calendar(_ calendar: Any, didSelectDate date: Date, ...) { selectedDate = date delegate?.calendarViewController(self, didSelectDate: date) navigationController?.popViewController(animated: true) } }
步骤3:主控制器遵守代理协议
class ViewController: UIViewController, CalendarViewControllerDelegate { var myDate = Date() override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showCalendar" { let calendarVC = segue.destination as! CalendarViewController calendarVC.delegate = self } } // 实现代理方法 func calendarViewController(_ vc: CalendarViewController, didSelectDate date: Date) { self.myDate = date updateDateLabel(date) } // ... 其他代码 }
再也不要用Show segue来回传值啦!它只会创建新的控制器实例,永远达不到你要的效果。优先用Unwind Segue(如果依赖Storyboard)或者闭包(代码更轻量),这两种方式都是直接和原来的主控制器实例交互,能保证变量更新生效。
内容的提问来源于stack exchange,提问作者BenJ

