Harish
Harish

Reputation: 125

UISplitViewController does not show DetailView on restoring state

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

Answers (0)

Related Questions