ChildView in FullScreenCover is recreated every time if ContentView is updated

Hi guys.
I have a question related to the next behavior.

I have ContentView with a list of views where the corresponding view models are passed. The user can click by some view. At the moment full-screen modal dialog will be shown according to the passed type. It's fine.
At some time my view models are being updated and the whole ContentView will be reloaded. The problem is: fullScreenCover is called and ChildEventView is recreated. How to prevent recreating ChildEventView?

Code Block language
struct ContentView: View {
@ObservedObject private var eventListViewModel = EventListViewModel()
@State private var fullScreenType: FullScreenType?
/* some stuff */
var body: some View {
ScrollView {
LazyVStack {
ForEach(eventListViewModel.cardStates.indices, id: \.self) { index in
let eventVM = eventListViewModel.eventVMs[index]
EventCardView(eventViewModel: eventVM, eventId: $selectedEvent.eventId) {
self.fullScreenType = .type1
}
/* some other views */
}
}
}
.fullScreenCover(item: $fullScreenType, onDismiss: {
self.fullScreenType = nil
}, content: { fullScreenType in
switch fullScreenType {
case .type1:
return ChildEventView(selectedEvent.eventId).eraseToAnyView()
/* some other cases */
}
})
}
}


fullScreenCover is called and ChildEventView is recreated.

I cannot reproduce the issue with the shown code. Maybe some hidden parts of your code may be affecting.
Can you show enough code to reproduce your issue?

But generally, initializers of any subviews may be called at any time in SwiftUI. You should not write code depending on when or how many times init is called.

I think that's to be expected.


The body will be created every time eventListViewModel changed. And the fullScreenCover , you can think it and it's subviews as part of the body. And I wrote a demo code as below:

class Time: ObservableObject {
    @Published var second = 0

    init(second: Int = 0) {
        self.second = second
    }

    func start() {
        Task {
            for _ in 0 ... 999 {
                try await Task.sleep(nanoseconds: 1000000000)
                second += 1
            }
        }
    }
}

struct ContentView2: View {
    @StateObject private var time = Time()
    @State private var isPresented = false

    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("\(time.second)")
        }
        .onAppear {
            time.start()
        }
        .onTapGesture {
            isPresented = true
        }
        .padding()
        .fullScreenCover(isPresented: $isPresented) {
            Text("\((0 ... 1000).randomElement() ?? 0)")
                .onTapGesture {
                    isPresented = false
                }
        }
    }
}

the time will change every one second which will cause ContentView2's body property re-render every one second. fullScreenCover display a Text which shows a random number. if the fullScreenCover is presented, you will see the text changes every one second. So how to avoid this "issue" ? Here's code:

class Time: ObservableObject {
    @Published var second = 0

    init(second: Int = 0) {
        self.second = second
    }

    func start() {
        Task {
            for _ in 0 ... 999 {
                try await Task.sleep(nanoseconds: 1000000000)
                second += 1
            }
        }
    }
}

struct TimeView: View {
    @StateObject private var time = Time()

    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("\(time.second)")
        }
        .onAppear {
            time.start()
        }
    }
}

struct ContentView: View {
    @State private var isPresented = false

    var body: some View {
        TimeView()
        .onTapGesture {
            isPresented = true
        }
        .padding()
        .fullScreenCover(isPresented: $isPresented) {
            Text("\((0 ... 1000).randomElement() ?? 0)")
                .onTapGesture {
                    isPresented = false
                }
        }
    }
}

Extract the view with timer into a new view will solve the issue.

ChildView in FullScreenCover is recreated every time if ContentView is updated
 
 
Q