视图控制器导航与状态保持的核心技术问题
Hey Jacob, great question—this is a super common gotcha when working with navigation controllers and segues. Since segues create a new instance of the destination view controller every time you trigger them, your button color changes don’t stick by default. Let’s walk through three practical solutions depending on your needs:
If you want to preserve the exact state of VC B (not just the button color, but any other user interactions or data), the simplest approach is to keep a reference to the B instance in VC A and reuse it instead of creating a new one each time.
Step 1: Add a property to VC A to hold the B instance:
class AViewController: UIViewController { private var vcB: BViewController? // ... rest of your code }Step 2: Replace the segue with a manual push action (you can delete the segue from your storyboard and add an IBAction to your "Go to B" button):
@IBAction func navigateToBTapped(_ sender: UIButton) { if let existingVCB = vcB { // Push the already-existing instance navigationController?.pushViewController(existingVCB, animated: true) } else { // First time: instantiate from storyboard and save the reference guard let newVCB = storyboard?.instantiateViewController(withIdentifier: "BViewController") as? BViewController else { return } vcB = newVCB navigationController?.pushViewController(newVCB, animated: true) } }
This way, every time you navigate to B, you’re using the same instance—so all your button color changes (and any other state) will be preserved. Just note: this keeps VC B in memory as long as VC A exists, so it’s best for lightweight view controllers.
If you don’t want to keep VC B in memory, or need the state to persist even if the app restarts, save the button color to a shared storage (like a view model or UserDefaults), then restore it when VC B loads.
Example with a View Model (cleaner than UserDefaults for app state):
Create a shared view model to hold the state:
class BViewModel { static let shared = BViewModel() var buttonBackgroundColor: UIColor = .systemBlue // Default color }In VC B, update the view model when the button is tapped:
@IBAction func changeColorTapped(_ sender: UIButton) { let newColor = UIColor.systemRed // Your desired color sender.backgroundColor = newColor BViewModel.shared.buttonBackgroundColor = newColor }Restore the color when VC B appears:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) changeColorButton.backgroundColor = BViewModel.shared.buttonBackgroundColor }
This works even if you use segues (since each new B instance will pull the latest state from the view model).
If you prefer to create a new instance of B every time, but still want to carry over the button color, pass the state from B back to A during the unwind, then forward it to the new B instance when you segue again.
Step 1: Add an unwind action to VC A to capture the state from B:
class AViewController: UIViewController { private var savedButtonColor: UIColor? @IBAction func unwindToA(_ segue: UIStoryboardSegue) { if let sourceVC = segue.source as? BViewController { savedButtonColor = sourceVC.changeColorButton.backgroundColor } } // ... }(Don’t forget to connect the unwind segue in your storyboard: drag from VC B’s exit icon to the "Unwind to A" action.)
Step 2: Pass the saved color to the new B instance in VC A’s
prepare(for:):override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "ToVCB", let destinationVC = segue.destination as? BViewController { destinationVC.preferredButtonColor = savedButtonColor } }Step 3: Add a property to VC B and apply the color when it loads:
class BViewController: UIViewController { var preferredButtonColor: UIColor? override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) if let color = preferredButtonColor { changeColorButton.backgroundColor = color } } // ... }
Which one should you choose?
- Go with Solution 1 if you need to preserve the entire state of VC B (e.g., text inputs, scroll position, etc.).
- Choose Solution 2 if you want persistent state (even across app restarts) or don’t want to hold onto VC B’s instance.
- Use Solution 3 if you prefer fresh instances of B but need to carry over specific state values.
内容的提问来源于stack exchange,提问作者Jacob

