1

I have a UI where you can navigate/push views like this: AView -> BView -> CView -> DView

I want to pop a couple of views (get to BView from DView) instead of placing a new view (BView) on top of existing stack and I found the way to do this:

(UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.toBView()

What I don't understand is why does it call CView's init() when I try to return from DView to BView?

Here's the output i get in debug console:

AView init
BView init
CView init
DView init
contract.
button pressed
BView init
**CView init**

Why does it call CView's init() and how to avoid this behaviour?

AView.swift:

import SwiftUI

struct AView: View {
    
    init() {
        print("AView init")
    }
    

    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: BView()) {
                    Text("This is View A, now go to View B.")
                }
            }
        }
    }
}

struct BView: View {
    init() {
        print("BView init")
    }
    

    var body: some View {
        NavigationLink(destination: CView()) {
                Text("This is View B, now go to View C.")
        }
    }
}

struct CView: View {
    
    init() {
        print("CView init")
    }
    
    var body: some View {
        NavigationLink(destination: DView()) {
                Text("This is View C, now go to View D.")
        }
    }
}

struct DView: View {
    init() {
        print("DView init")
    }
    
    var body: some View {
        Button(action: {
            print("button pressed")
            (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.toBView()

        },
               label: {
            Text("Back!")
        })

    }
}

SceneDelegate.swift:

import UIKit
import SwiftUI

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Create the SwiftUI view that provides the window contents.
        let aView =     AView()
        
        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: AView)
            self.window = window
            window.makeKeyAndVisible()
        }
    }
    
    func toBView() {
        let bView = BView()
        window?.rootViewController = UIHostingController(rootView: bView)
    }

}
eugene_prg
  • 948
  • 2
  • 12
  • 25

1 Answers1

1

This behavior is not wrong. If you start your app and display AView you will see in the console that it prints init from A and B.

It's because BView() is already initialized in your AView, even though you haven't activated the NavigationLink.

NavigationLink(destination: BView()) {

So this behavior is not specific to your pop back action, happens already at the beginning. See this solution from Asperi regarding multiple init() aswell, if you are concerned about calling init() more than once

davidev
  • 7,694
  • 5
  • 21
  • 56