1

I'm unable to get State Restoration working with a Navigation Controller. I am using Swift and do not want to use Storyboards (Programmatic). Almost all the help I've found online is either not in Swift or using Storyboards.

In the demo code below, ViewController contains a simple PickerView, and a selection variable keeps track of the Picker selection. The AppDelegate is presented with 2 options. With Option 1, no Navigation Controller is used and the state of the Picker is restored fine, but is not restored with the Navigation Controller in Option 2. (In the code below, Option 1 is commented out and Option 2 is active).

You can copy & paste the code below into a fresh singleView application and it should reproduce what I've described. (I tested it)

AppDelegate Code:

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

  var window: UIWindow?

  func application(_ application: UIApplication, shouldSaveApplicationState coder: NSCoder) -> Bool {
    return true
  }
  func application(_ application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool {
    return true
  }

  func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
    window = UIWindow(frame: UIScreen.main.bounds)
    window?.makeKeyAndVisible()
    let pickerVC = ViewController()
    //Option 1: No NavC used
    //window?.rootViewController = pickerVC

    //Option 2: NavC used
    let navC = UINavigationController(rootViewController: pickerVC)
    navC.restorationIdentifier = "PickerNav"
    window?.rootViewController = navC

    return true
  }

//  func application(_ application: UIApplication, viewControllerWithRestorationIdentifierPath identifierComponents: [Any], coder: NSCoder) -> UIViewController? {
//    let storyboard = UIStoryboard(name: "Main", bundle: nil)
//      if let lastItem = identifierComponents.last as? String {
//        return storyboard.instantiateViewController(withIdentifier: lastItem)
//      }
//    return nil
//  }
}

ViewController Code:

import UIKit

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

  var pickerView: UIPickerView!
  var selection = 0
  let group = ["Fruit","Vegetable","Meat","Bread"]

  override func viewDidLoad() {
    super.viewDidLoad()
    restorationIdentifier = "PickerVC"
    setupPicker()
  }

  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    pickerView.selectRow(selection, inComponent: 0, animated: false)
  }

  override func encodeRestorableState(with coder: NSCoder) {
    coder.encode(selection, forKey: "selection")
    super.encodeRestorableState(with: coder)
  }

  override func decodeRestorableState(with coder: NSCoder) {
    selection = coder.decodeInteger(forKey: "selection")
    super.decodeRestorableState(with: coder)
  }

  func setupPicker() {
    pickerView = UIPickerView()
    pickerView.delegate = self
    pickerView.dataSource = self
    view.addSubview(pickerView)

    pickerView.translatesAutoresizingMaskIntoConstraints = false
    pickerView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
    pickerView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor).isActive = true
    pickerView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    pickerView.heightAnchor.constraint(equalToConstant: 300).isActive = true
  }

  func numberOfComponents(in pickerView: UIPickerView) -> Int {
    return 1
  }

  func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return group.count
  }

  func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    selection = pickerView.selectedRow(inComponent: 0)
  }

  func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
    let pickerLabel = UILabel()
    pickerLabel.font = UIFont.systemFont(ofSize: 26)
    pickerLabel.textAlignment = .center
    pickerLabel.text = group[row]
    pickerLabel.textColor = .white
    return pickerLabel
  }
}

Details of my testing: With Option 1, changing the Picker followed by cmd-Shift-H causes the selection variable to be saved in encodeRestorableState. I then click the Xcode Stop button and then run it again, and the selection variable is restored in decodeRestorableState. By contrast, with Option 2 State restoration does not work because decodeRestorableState is never called so the selection variable is not restored. However, a breakpoint at viewDidAppear shows that navigationController?.restorationIdentifier = "PickerNav" and restorationIdentifier = "PickerVC"

From what I've read, I suspect I may need to use viewControllerWithRestorationIdentifierPath in AppDelegate, but I didn't know how to use it correctly. My attempt at the bottom of AppDelegate (the code is commented out) causes the app to crash.

4

1 回答 1

2

You can add an extension to your ViewController and make it conform to protocol UIViewControllerRestoration and implement the viewControllerWithRestorationIdentifierPath method. In the ViewController's viewDidLoad function add restorationClass just like restorationIdentifer like

    override func viewDidLoad() {
        super.viewDidLoad()
        restorationIdentifier = "PickerVC"
        restorationClass = ViewController.self
        setupPicker()
    }


extension ViewController: UIViewControllerRestoration {

    static func viewController(withRestorationIdentifierPath identifierComponents: [Any], coder: NSCoder) -> UIViewController? {
        let vc = ViewController()
        return vc
    }

}

Add this code in your ViewController class and delete the viewControllerWithRestorationIdentifierPath method that you have added in AppDelegate

于 2017-08-21T09:08:07.637 回答