Reputation: 125
I have an app that enables state restoration. The app is structured as follows - The entry point is a UIViewController, which on press of a button can present a UISplitViewController with master and detail view controllers. The master view controller opens the Detail view controller on tap of a button.
When terminating the app from the Detail ViewController and relaunching the app, it restores the state of the Master ViewController and not the Detail ViewController.
I have provided restorationIds for all the view controllers and navigation controllers, the view controllers are created programmatically and I have provided the restorationClass for them.
Here's my AppDelegate
//
// AppDelegate.swift
// TestNav
//
//
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window:UIWindow?
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
let viewController = ViewController()
let navViewController = UINavigationController(rootViewController: viewController)
navViewController.restorationIdentifier = "entryNav"
window = UIWindow(frame: UIScreen.main.bounds)
window?.restorationIdentifier = "appWindow"
window?.makeKeyAndVisible()
window?.rootViewController = navViewController
return true
}
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window?.makeKeyAndVisible()
return true
}
func application(_ application: UIApplication, shouldSaveSecureApplicationState coder: NSCoder) -> Bool {
return true
}
func application(_ application: UIApplication, shouldRestoreSecureApplicationState coder: NSCoder) -> Bool {
return true
}
}
ViewController.swift
//
// ViewController.swift
// TestNav
//
//
import UIKit
class ViewController: UIViewController, UIViewControllerRestoration {
lazy var goNext: UIButton = {
let button = UIButton()
button.setTitle("Go to SplitVC", for: .normal)
button.setTitleColor(.black, for: .normal)
button.restorationIdentifier = "goToSplit"
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(goToSplitVC), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
self.restorationIdentifier = "entryVC"
self.restorationClass = ViewController.self
self.title = "Start"
view.backgroundColor = .white
view.addSubview(goNext)
NSLayoutConstraint.activate([
goNext.centerXAnchor.constraint(equalTo: view.centerXAnchor),
goNext.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
override func encodeRestorableState(with coder: NSCoder) {
super.encodeRestorableState(with: coder)
}
override func decodeRestorableState(with coder: NSCoder) {
super.decodeRestorableState(with: coder)
}
static func viewController(
withRestorationIdentifierPath identifierComponents: [String],
coder: NSCoder
) -> UIViewController? {
let entryVC = ViewController()
entryVC.restorationIdentifier = "entryVC"
return entryVC
}
@objc func goToSplitVC() {
let masterVC = MasterVC()
masterVC.restorationIdentifier = "MasterVC"
let splitVC = CustomSplitVC()
let masterNavController = UINavigationController(rootViewController: masterVC)
masterNavController.restorationIdentifier = "masterNav"
splitVC.viewControllers = [masterNavController]
splitVC.preferredDisplayMode = .allVisible
self.presentViewController(fromVC: self, toVC: splitVC)
}
func presentViewController(fromVC: UIViewController, toVC: UIViewController) {
toVC.modalPresentationStyle = .fullScreen
toVC.modalTransitionStyle = .crossDissolve
fromVC.present(toVC, animated: true, completion: nil)
}
}
Here's my custom split view controller
class CustomSplitVC: UISplitViewController, UIViewControllerRestoration {
override func viewDidLoad() {
restorationIdentifier = "CustomSplit"
restorationClass = CustomSplitVC.self
super.viewDidLoad()
self.modalPresentationStyle = .fullScreen
self.modalTransitionStyle = .crossDissolve
}
static func viewController(
withRestorationIdentifierPath identifierComponents: [String],
coder: NSCoder
) -> UIViewController? {
let masterVC = MasterVC()
masterVC.restorationIdentifier = "MasterVC"
let splitVC = CustomSplitVC()
let masterNavController = UINavigationController(rootViewController: masterVC)
masterNavController.restorationIdentifier = "masterNav"
splitVC.viewControllers = [masterNavController]
splitVC.preferredDisplayMode = .allVisible
return splitVC
}
override func encodeRestorableState(with coder: NSCoder) {
super.encodeRestorableState(with: coder)
}
override func decodeRestorableState(with coder: NSCoder) {
super.decodeRestorableState(with: coder)
}
}
Master and Detail viewControllers
//
// MasterVC.swift
// TestNav
//
//
import UIKit
class MasterVC: UIViewController, UIViewControllerRestoration {
lazy var goNext: UIButton = {
let button = UIButton()
button.setTitle("Show Details", for: .normal)
button.setTitleColor(.black, for: .normal)
button.restorationIdentifier = "goNext"
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(showDetails), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
self.restorationIdentifier = "MasterVC"
self.restorationClass = MasterVC.self
modalPresentationStyle = .fullScreen
modalTransitionStyle = .crossDissolve
view.backgroundColor = .yellow
self.title = "List"
super.viewDidLoad()
view.addSubview(goNext)
NSLayoutConstraint.activate([
goNext.centerXAnchor.constraint(equalTo: view.centerXAnchor),
goNext.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
let backButton = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(backButtonTapped))
backButton.tintColor = .white
navigationItem.leftBarButtonItem = backButton
}
@objc func backButtonTapped() {
dismiss(animated: true, completion: nil)
}
static func viewController(
withRestorationIdentifierPath identifierComponents: [String],
coder: NSCoder
) -> UIViewController? {
let vc = MasterVC()
vc.restorationIdentifier = "MasterVC"
return vc
}
override func encodeRestorableState(with coder: NSCoder) {
super.encodeRestorableState(with: coder)
}
override func decodeRestorableState(with coder: NSCoder) {
super.decodeRestorableState(with: coder)
//showDetails()
}
@objc func showDetails() {
let detailVC = SecondVC()
detailVC.restorationIdentifier = "SecondVC"
let navController = UINavigationController(rootViewController: detailVC)
navController.restorationIdentifier = "secondNav"
splitViewController?.showDetailViewController(navController, sender: self)
}
}
Detail ViewController
//
// SecondVC.swift
// TestNav
//
import UIKit
class SecondVC: UIViewController, UIViewControllerRestoration {
var headerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(hex: 0x464C68)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
self.restorationIdentifier = "SecondVC"
self.restorationClass = SecondVC.self
view.backgroundColor = .white
super.viewDidLoad()
self.title = "Detail"
view.addSubview(headerView)
NSLayoutConstraint.activate([
headerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
headerView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
headerView.trailingAnchor.constraint(equalTo:view.safeAreaLayoutGuide.trailingAnchor),
headerView.heightAnchor.constraint(equalToConstant: 200)
])
}
deinit {
print("Deinit SecondVC")
}
override func encodeRestorableState(with coder: NSCoder) {
super.encodeRestorableState(with: coder)
}
override func decodeRestorableState(with coder: NSCoder) {
super.decodeRestorableState(with: coder)
self.title = "Restored Detail"
}
static func viewController(
withRestorationIdentifierPath identifierComponents: [String],
coder: NSCoder
) -> UIViewController? {
let secondVC = SecondVC()
secondVC.restorationIdentifier = "SecondVC"
return secondVC
}
}
The app tries to restore the Detail view controller's state by invoking viewController(withRestorationIdentifierPath:) and but then it gets de-initialized after invoking viewDidLoad()
Upvotes: 0
Views: 31