Ios 管理视图控制器以维护状态

Ios 管理视图控制器以维护状态,ios,swift,xcode,viewcontroller,Ios,Swift,Xcode,Viewcontroller,关于如何在视图控制器之间导航的基本问题。假设我有VC“A”和VC“B”,它们都嵌入在导航控制器中。从A到B,我用一个音阶。在我到达B之后,我点击一个按钮,它会做出一些视觉上的改变,比如按钮的颜色。然后我点击导航控制器的后退按钮放松,回到A。然后我再次使用segue转到B,我得到一个新的VC“B”-没有新的按钮颜色。我想这就是segues的工作原理 但是我如何用新的按钮颜色获得相同的VC B呢 我的技巧是保存VC B的一个实例变量(在VC A的类中),并使用navigationController

关于如何在视图控制器之间导航的基本问题。假设我有VC“A”和VC“B”,它们都嵌入在导航控制器中。从A到B,我用一个音阶。在我到达B之后,我点击一个按钮,它会做出一些视觉上的改变,比如按钮的颜色。然后我点击导航控制器的后退按钮放松,回到A。然后我再次使用segue转到B,我得到一个新的VC“B”-没有新的按钮颜色。我想这就是segues的工作原理

但是我如何用新的按钮颜色获得相同的VC B呢

我的技巧是保存VC B的一个实例变量(在VC A的类中),并使用navigationController.pushViewController()导航到它。测试确认我的按钮颜色变化在调用B之间保持不变。但是这个解决方案感觉很笨拙


所以,在我充实这段代码之前,我需要问:有没有一种设计模式可以让a->B->a->B而第二次不得到一个新的B?首选的方法是什么?

当您按下查看控制器B时,会创建B的实例,这就是您按下的方法。当您弹回到视图控制器A时,视图控制器B的实例将被取消初始化,永远消失。当您推回视图控制器B时,您将创建一个新实例。因此,要使按钮的颜色保持不变,需要保留视图控制器B的状态,以便其所有实例都反映该更改。有许多方法可以保持状态,您选择的选项将由项目和个人偏好决定。

当您按下查看控制器B时,会创建B的实例,这就是您按下的对象。当您弹回到视图控制器A时,视图控制器B的实例将被取消初始化,永远消失。当您推回视图控制器B时,您将创建一个新实例。因此,要使按钮的颜色保持不变,需要保留视图控制器B的状态,以便其所有实例都反映该更改。保存状态的方法有很多种,您选择的选项将由项目和个人偏好决定。

UINavigationController
释放弹出视图控制器(VC B),因此当您再次尝试推送VC B时,会创建一个新实例。除非您在VC a中持有对VC B的强引用。不建议这样做,但VC a仍保留在导航堆栈中,因此如果它持有对VC B的引用,则该实例可能会被推回

推荐的方法是在弹出VC B时获取要维护的状态,并在推送新的VC B实例时将其设置回原位

解决方案1(不推荐):

class VCA: UIViewController {
    lazy var vcB: VCB = VCB()

    func presentVCB() {
        navigationController?.pushViewController(vcB, animated: true)
    }
}

class VCB: UIViewController {
    var someState: String = ""
}
解决方案2,使用委派:

class VCA: UIViewController, VCBDelegate {

    // This could be any data-structure in which you can store the states in VC B
    var lastVCBState: String?

    func presentVCB() {
        let vcB: VCB = VCB()
        vcB.delegate = self
        lastVCBState != nil ? vcB.someState = lastVCBState! : ()
        navigationController?.pushViewController(vcB, animated: true)
    }

    // MARK: VCBDelegate
    func didPopWith(state: String) {
        lastVCBState = state
    }
}

protocol VCBDelegate: class {
    func didPopWith(state: String)
}

class VCB: UIViewController {
    weak var delegate: VCBDelegate?

    // Any data-structure in which you can store the states
    var someState: String = ""

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        delegate?.didPopWith(state: someState)
    }
}
class VCB: UIViewController {

   @IBOutlet weak var myButton: UIButton!

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
        self.myButton.backgroundColor = UIHelper.sharedInstance.VCBButtonColor
   }

   @IBAction func changeColorButtonAction(_ sender: UIButton) {
      //Save in the color in singleton class
      UIHelper.sharedInstance.VCBButtonColor = UIColor.green
   }
}

//Singleton Class
class UIHelper: NSObject {
    var VCBButtonColor = UIColor.red
    static let sharedInstance = UIHelperClass()    

    override init() {
       ...
    }
}
class VCB: UIViewController {

   @IBOutlet weak var myButton: UIButton!

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
          if let savedColor = UserDefaults.standard.color(forKey: "SavedColor") {
             self.myButton.backgroundColor = savedColor
          }
   }

   @IBAction func changeColorButtonAction(_ sender: UIButton) {
       UserDefaults.standard.set(color: UIColor.red, forKey: "SavedColor")
   }
}

extension UserDefaults {
   func color(forKey key: String) -> UIColor? {
      var color: UIColor?
      if let colorData = data(forKey: key) {
         color = NSKeyedUnarchiver.unarchiveObject(with: colorData) as? UIColor
      }
      return color
   }

   func set(color: UIColor?, forKey key: String) {
      var colorData: NSData?
      if let color = color {
         colorData = NSKeyedArchiver.archivedData(withRootObject: color) as NSData?
      }
      set(colorData, forKey: key)
   }
}

UINavigationController
取消分配弹出的视图控制器(VC B),因此当您再次尝试推送VC B时,会创建一个新实例。除非您在VC a中持有对VC B的强引用。不建议这样做,但VC a仍保留在导航堆栈中,因此如果它持有对VC B的引用,则该实例可能会被推回

推荐的方法是在弹出VC B时获取要维护的状态,并在推送新的VC B实例时将其设置回原位

解决方案1(不推荐):

class VCA: UIViewController {
    lazy var vcB: VCB = VCB()

    func presentVCB() {
        navigationController?.pushViewController(vcB, animated: true)
    }
}

class VCB: UIViewController {
    var someState: String = ""
}
解决方案2,使用委派:

class VCA: UIViewController, VCBDelegate {

    // This could be any data-structure in which you can store the states in VC B
    var lastVCBState: String?

    func presentVCB() {
        let vcB: VCB = VCB()
        vcB.delegate = self
        lastVCBState != nil ? vcB.someState = lastVCBState! : ()
        navigationController?.pushViewController(vcB, animated: true)
    }

    // MARK: VCBDelegate
    func didPopWith(state: String) {
        lastVCBState = state
    }
}

protocol VCBDelegate: class {
    func didPopWith(state: String)
}

class VCB: UIViewController {
    weak var delegate: VCBDelegate?

    // Any data-structure in which you can store the states
    var someState: String = ""

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        delegate?.didPopWith(state: someState)
    }
}
class VCB: UIViewController {

   @IBOutlet weak var myButton: UIButton!

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
        self.myButton.backgroundColor = UIHelper.sharedInstance.VCBButtonColor
   }

   @IBAction func changeColorButtonAction(_ sender: UIButton) {
      //Save in the color in singleton class
      UIHelper.sharedInstance.VCBButtonColor = UIColor.green
   }
}

//Singleton Class
class UIHelper: NSObject {
    var VCBButtonColor = UIColor.red
    static let sharedInstance = UIHelperClass()    

    override init() {
       ...
    }
}
class VCB: UIViewController {

   @IBOutlet weak var myButton: UIButton!

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
          if let savedColor = UserDefaults.standard.color(forKey: "SavedColor") {
             self.myButton.backgroundColor = savedColor
          }
   }

   @IBAction func changeColorButtonAction(_ sender: UIButton) {
       UserDefaults.standard.set(color: UIColor.red, forKey: "SavedColor")
   }
}

extension UserDefaults {
   func color(forKey key: String) -> UIColor? {
      var color: UIColor?
      if let colorData = data(forKey: key) {
         color = NSKeyedUnarchiver.unarchiveObject(with: colorData) as? UIColor
      }
      return color
   }

   func set(color: UIColor?, forKey key: String) {
      var colorData: NSData?
      if let color = color {
         colorData = NSKeyedArchiver.archivedData(withRootObject: color) as NSData?
      }
      set(colorData, forKey: key)
   }
}

第1步:更改底部颜色时,以任何持久方式保存。 例如,Singleton类或NSUserDefaults

步骤2:将保存的颜色加载到VCB中

使用单例类的示例:

class VCA: UIViewController, VCBDelegate {

    // This could be any data-structure in which you can store the states in VC B
    var lastVCBState: String?

    func presentVCB() {
        let vcB: VCB = VCB()
        vcB.delegate = self
        lastVCBState != nil ? vcB.someState = lastVCBState! : ()
        navigationController?.pushViewController(vcB, animated: true)
    }

    // MARK: VCBDelegate
    func didPopWith(state: String) {
        lastVCBState = state
    }
}

protocol VCBDelegate: class {
    func didPopWith(state: String)
}

class VCB: UIViewController {
    weak var delegate: VCBDelegate?

    // Any data-structure in which you can store the states
    var someState: String = ""

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        delegate?.didPopWith(state: someState)
    }
}
class VCB: UIViewController {

   @IBOutlet weak var myButton: UIButton!

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
        self.myButton.backgroundColor = UIHelper.sharedInstance.VCBButtonColor
   }

   @IBAction func changeColorButtonAction(_ sender: UIButton) {
      //Save in the color in singleton class
      UIHelper.sharedInstance.VCBButtonColor = UIColor.green
   }
}

//Singleton Class
class UIHelper: NSObject {
    var VCBButtonColor = UIColor.red
    static let sharedInstance = UIHelperClass()    

    override init() {
       ...
    }
}
class VCB: UIViewController {

   @IBOutlet weak var myButton: UIButton!

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
          if let savedColor = UserDefaults.standard.color(forKey: "SavedColor") {
             self.myButton.backgroundColor = savedColor
          }
   }

   @IBAction func changeColorButtonAction(_ sender: UIButton) {
       UserDefaults.standard.set(color: UIColor.red, forKey: "SavedColor")
   }
}

extension UserDefaults {
   func color(forKey key: String) -> UIColor? {
      var color: UIColor?
      if let colorData = data(forKey: key) {
         color = NSKeyedUnarchiver.unarchiveObject(with: colorData) as? UIColor
      }
      return color
   }

   func set(color: UIColor?, forKey key: String) {
      var colorData: NSData?
      if let color = color {
         colorData = NSKeyedArchiver.archivedData(withRootObject: color) as NSData?
      }
      set(colorData, forKey: key)
   }
}
注意:当您使用singleton类并关闭应用程序时,您将丢失保存的颜色

使用用户默认值的示例:

class VCA: UIViewController, VCBDelegate {

    // This could be any data-structure in which you can store the states in VC B
    var lastVCBState: String?

    func presentVCB() {
        let vcB: VCB = VCB()
        vcB.delegate = self
        lastVCBState != nil ? vcB.someState = lastVCBState! : ()
        navigationController?.pushViewController(vcB, animated: true)
    }

    // MARK: VCBDelegate
    func didPopWith(state: String) {
        lastVCBState = state
    }
}

protocol VCBDelegate: class {
    func didPopWith(state: String)
}

class VCB: UIViewController {
    weak var delegate: VCBDelegate?

    // Any data-structure in which you can store the states
    var someState: String = ""

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        delegate?.didPopWith(state: someState)
    }
}
class VCB: UIViewController {

   @IBOutlet weak var myButton: UIButton!

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
        self.myButton.backgroundColor = UIHelper.sharedInstance.VCBButtonColor
   }

   @IBAction func changeColorButtonAction(_ sender: UIButton) {
      //Save in the color in singleton class
      UIHelper.sharedInstance.VCBButtonColor = UIColor.green
   }
}

//Singleton Class
class UIHelper: NSObject {
    var VCBButtonColor = UIColor.red
    static let sharedInstance = UIHelperClass()    

    override init() {
       ...
    }
}
class VCB: UIViewController {

   @IBOutlet weak var myButton: UIButton!

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
          if let savedColor = UserDefaults.standard.color(forKey: "SavedColor") {
             self.myButton.backgroundColor = savedColor
          }
   }

   @IBAction func changeColorButtonAction(_ sender: UIButton) {
       UserDefaults.standard.set(color: UIColor.red, forKey: "SavedColor")
   }
}

extension UserDefaults {
   func color(forKey key: String) -> UIColor? {
      var color: UIColor?
      if let colorData = data(forKey: key) {
         color = NSKeyedUnarchiver.unarchiveObject(with: colorData) as? UIColor
      }
      return color
   }

   func set(color: UIColor?, forKey key: String) {
      var colorData: NSData?
      if let color = color {
         colorData = NSKeyedArchiver.archivedData(withRootObject: color) as NSData?
      }
      set(colorData, forKey: key)
   }
}

第1步:更改底部颜色时,以任何持久方式保存。 例如,Singleton类或NSUserDefaults

步骤2:将保存的颜色加载到VCB中

使用单例类的示例:

class VCA: UIViewController, VCBDelegate {

    // This could be any data-structure in which you can store the states in VC B
    var lastVCBState: String?

    func presentVCB() {
        let vcB: VCB = VCB()
        vcB.delegate = self
        lastVCBState != nil ? vcB.someState = lastVCBState! : ()
        navigationController?.pushViewController(vcB, animated: true)
    }

    // MARK: VCBDelegate
    func didPopWith(state: String) {
        lastVCBState = state
    }
}

protocol VCBDelegate: class {
    func didPopWith(state: String)
}

class VCB: UIViewController {
    weak var delegate: VCBDelegate?

    // Any data-structure in which you can store the states
    var someState: String = ""

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        delegate?.didPopWith(state: someState)
    }
}
class VCB: UIViewController {

   @IBOutlet weak var myButton: UIButton!

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
        self.myButton.backgroundColor = UIHelper.sharedInstance.VCBButtonColor
   }

   @IBAction func changeColorButtonAction(_ sender: UIButton) {
      //Save in the color in singleton class
      UIHelper.sharedInstance.VCBButtonColor = UIColor.green
   }
}

//Singleton Class
class UIHelper: NSObject {
    var VCBButtonColor = UIColor.red
    static let sharedInstance = UIHelperClass()    

    override init() {
       ...
    }
}
class VCB: UIViewController {

   @IBOutlet weak var myButton: UIButton!

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
          if let savedColor = UserDefaults.standard.color(forKey: "SavedColor") {
             self.myButton.backgroundColor = savedColor
          }
   }

   @IBAction func changeColorButtonAction(_ sender: UIButton) {
       UserDefaults.standard.set(color: UIColor.red, forKey: "SavedColor")
   }
}

extension UserDefaults {
   func color(forKey key: String) -> UIColor? {
      var color: UIColor?
      if let colorData = data(forKey: key) {
         color = NSKeyedUnarchiver.unarchiveObject(with: colorData) as? UIColor
      }
      return color
   }

   func set(color: UIColor?, forKey key: String) {
      var colorData: NSData?
      if let color = color {
         colorData = NSKeyedArchiver.archivedData(withRootObject: color) as NSData?
      }
      set(colorData, forKey: key)
   }
}
注意:当您使用singleton类并关闭应用程序时,您将丢失保存的颜色

使用用户默认值的示例:

class VCA: UIViewController, VCBDelegate {

    // This could be any data-structure in which you can store the states in VC B
    var lastVCBState: String?

    func presentVCB() {
        let vcB: VCB = VCB()
        vcB.delegate = self
        lastVCBState != nil ? vcB.someState = lastVCBState! : ()
        navigationController?.pushViewController(vcB, animated: true)
    }

    // MARK: VCBDelegate
    func didPopWith(state: String) {
        lastVCBState = state
    }
}

protocol VCBDelegate: class {
    func didPopWith(state: String)
}

class VCB: UIViewController {
    weak var delegate: VCBDelegate?

    // Any data-structure in which you can store the states
    var someState: String = ""

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        delegate?.didPopWith(state: someState)
    }
}
class VCB: UIViewController {

   @IBOutlet weak var myButton: UIButton!

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
        self.myButton.backgroundColor = UIHelper.sharedInstance.VCBButtonColor
   }

   @IBAction func changeColorButtonAction(_ sender: UIButton) {
      //Save in the color in singleton class
      UIHelper.sharedInstance.VCBButtonColor = UIColor.green
   }
}

//Singleton Class
class UIHelper: NSObject {
    var VCBButtonColor = UIColor.red
    static let sharedInstance = UIHelperClass()    

    override init() {
       ...
    }
}
class VCB: UIViewController {

   @IBOutlet weak var myButton: UIButton!

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
          if let savedColor = UserDefaults.standard.color(forKey: "SavedColor") {
             self.myButton.backgroundColor = savedColor
          }
   }

   @IBAction func changeColorButtonAction(_ sender: UIButton) {
       UserDefaults.standard.set(color: UIColor.red, forKey: "SavedColor")
   }
}

extension UserDefaults {
   func color(forKey key: String) -> UIColor? {
      var color: UIColor?
      if let colorData = data(forKey: key) {
         color = NSKeyedUnarchiver.unarchiveObject(with: colorData) as? UIColor
      }
      return color
   }

   func set(color: UIColor?, forKey key: String) {
      var colorData: NSData?
      if let color = color {
         colorData = NSKeyedArchiver.archivedData(withRootObject: color) as NSData?
      }
      set(colorData, forKey: key)
   }
}

可以使用“navigationController”作为入口点在ViewController之间进行切换,从该入口点可以切换到根VCA,然后切换到VCB。在这种情况下,VC的所有状态都被保留(但我认为陀螺仪和加速度计除外)。这在我的所有应用程序中都很好,即使是那些使用场景动画的应用程序:-)


Alex可以使用“navigationController”作为入口点在ViewController之间进行切换,您可以从该入口点切换到根VCA,然后切换到VCB。在这种情况下,VC的所有状态都被保留(但我认为陀螺仪和加速度计除外)。这在我的所有应用程序中都很好,即使是那些使用场景动画的应用程序:-)


亚历克斯

您不保存旧VC。B确保数据模型对象被更新,以便新的VC B能够正确地渲染其视图。您还可以考虑除了导航控制器之外的一些东西。听起来您想要的更接近于保留每个根视图控制器的选项卡栏控制器。或者,您可能需要构建自己的视图控制器,可以在A和B之间切换。谢谢Paul。你是说每次我做改变,我都要把它保存下来?有些东西甚至没有变量来存储它们,它们是如此的短暂——比如我选择了表中的哪些项。我不想把所有的东西都保存下来,谢谢你。我注意到我的VC A看起来确实很像一组标签!也许这就是我需要的。你只需要4个大按钮就可以把你带到4个不同的VC中。你不保存旧的VC。你可以确保你的数据模型对象被更新,以便新的VC B能够正确地渲染它的视图。你也可以考虑除了导航控制器之外的东西。听起来您想要的更接近于保留每个根视图控制器的选项卡栏控制器。或者你需要