How can I initialize View Again in SwiftUI?
Asked Answered
B

2

12

I’m using SwfitUI in my project and I have a NavigationView and List. I’m clicking cell after open the detail view and click navigation back button. I want to remove view (it’s struct, in SwiftUI) after click navigation back button. Because if I click the same cell or button again, it isn’t initialize new view, it’s shows old view. I want to do refresh this view. How do I do?

My FirstView Struct is:

struct FirstView: View {

    @ObservedObject var viewModel: FirstViewModel

    var body: some View {
        List(self.viewModel.objects, id: \.id) { object in
            ZStack {
                DetailViewCell(object: object)
                NavigationLink(destination: DetailViewBuilder.make(object)) {
                    EmptyView()
                }.buttonStyle(PlainButtonStyle())
            }
        }
    }
}

My DetailView Struct is:

struct DetailView: View {

    @ObservedObject var viewModel: DetailViewModel

    var body: some View {
        ZStack(alignment: .top) {
            Color.mainBackground.edgesIgnoringSafeArea(.all)
            VStack {
                ZStack {
                    Image("Square")
                    Image(self.viewModel.currentImage)
                }
                Text(self.viewModel.currentText)
                    .padding()
                    .frame(alignment: .center)
                    .minimumScaleFactor(0.1)
                Spacer()
                Button(action: {
                    self.viewModel.pressedPlayOrPauseButton()
                }, label: {
                    Image(self.viewModel.isPlaying ? "Pause" : "Play").foregroundColor(Color("Orange"))
                }).padding()
            }
        }
    }
}

First of all, I go to the detail by clicking a cell in FirstView. Then I come back with the back button. I click on a cell again to go to the details, but a new view does not open. It shows the old view.

Before I forget, My Builder Class is:

final class DetailViewBuilder {
    static func make(object: Something) -> DetailView {

        let viewModel = DetailViewModel(object: object)
        let view = DetailView(viewModel: viewModel)

        return view
    }
}

Note: If I will use Sheet Presented, It's working. It's creating new View. But I want to use NavigationLink. Thank you.

Ba answered 15/4, 2020 at 21:18 Comment(0)
D
25

You just need to defer your destination creation in your builder, and the @ViewBuilder is a good instrument for this.

It can be used the following wrapper for to create real destination only in when body will be rendered (ie. explicitly in time of navigation clicked in your case)

Tested with Xcode 11.4 / iOS 13.4

struct DeferView<Content: View>: View {
    let content: () -> Content

    init(@ViewBuilder _ content: @escaping () -> Content) {
        self.content = content
    }
    var body: some View {
        content()          // << everything is created here
    }
}

and now your builder

final class DetailViewBuilder {
    static func make(object: Something) -> some View {
        DeferView {
           DetailView(viewModel: DetailViewModel(object: object))
        }
    }
}
Dauphin answered 16/4, 2020 at 4:52 Comment(3)
Thank you. It's working. Really @ViewBuilder is a good solution.Ba
this is a great answer! it also solved my problem when destination view gets created multiple times as my current view gets refreshed multiple timesPisarik
Can anyone elaborate on why this works? Why doesn't this all get executed as usual, right away?Reduce
H
1

SwiftUI initialises NavigationView links eagerly, so you can't rely on their initialiser to run every time. If you write a custom init() for DetailView

    init(viewModel: DetailViewModel) {
    self.viewModel = viewModel
    print("Initialising \(self)")
}

you'll see the line print out for every navigation link when your FirstView loads.

Depending on what you want to do you could implement a .onAppear {} closure on the DetailView which will change the data when the view appears. It's a little tricky to try to offer implementation examples since I'm unsure what your goal here is specifically.

There's some interesting discussion of this at: https://www.notion.so/Lazy-Loading-Data-with-SwiftUI-and-Combine-70546ec6aca3481f80d104cd1f10a31a

Do any of the suggestions here look like they could be relevant to what you're trying to do?

Hayward answered 15/4, 2020 at 21:41 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.