HomeiOS Developmentios - SwiftUI not updating NavigationLink vacation spot

ios – SwiftUI not updating NavigationLink vacation spot


I’ve simplified all my code right down to this, you may run it to check:

ContentView.swift

import SwiftUI

struct ContentView: View {
    @StateObject var viewModel = ViewModel()
    
    var physique: some View {
        Record {
            ForEach($viewModel.databases, id: .title) { $database in
                NavigationLink(
                    vacation spot: {
                        VStack {
                            if database.isOpen {
                                Textual content("Opened!")
                            } else {
                                Textual content("Closed!")
                            }
                        }
                    },
                    label: {
                        DatabaseItem( database: $database )
                    }
                )
            }
            .environmentObject(viewModel)
        }
    }
}

class Database {
    var title: String
    var password: String?
    var isOpen: Bool { password != nil }
    
    init(_ title: String, password: String? = nil) {
        self.title = title
        self.password = password
    }
}

class ViewModel: ObservableObject {
    @Printed var databases: [Database] = [
        Database("main"), Database("test", password: "123")
    ]
    
    func shut(_ db: Database) {
        db.password = nil
    }
}

struct DatabaseItem: View {
    @Binding var database: Database
    @EnvironmentObject var viewModel: ViewModel

    var physique: some View {
        HStack {
            Picture(systemName: database.isOpen ? "lock.open.fill" : "lock.fill")
                .body(width: 32, top: 32)
            Textual content(database.title)
            Spacer()
        }
        .swipeActions {
            if database.isOpen {
                Button(
                    motion: { viewModel.shut(database) },
                    label: { Picture(systemName: "lock.fill") }
                )
            }
        }
    }
}

TestApp.swift

import SwiftUI

@fundamental
struct TestApp: App {
    var physique: some Scene {
        WindowGroup {
            NavigationStack {
                ContentView()
            }
        }
    }
}

The UI appears to be like like this:
enter image description here

Rationalization

The ViewModel holds a listing of databases. Every database will be in 2 states: opened or closed. If password of a database is nil – the database is closed, in any other case it is opened (as is represented by the isOpen computed property).

We draw a listing of database objects: a reputation of every database, and a lock icon representing whether or not the database is opened or closed.

When you choose a database merchandise from the record, you go to the following display screen. If you choose opened database, you go to the display screen that claims “Opened!”. If you happen to chosen closed database, you go to display screen saying “Closed!”.

Now, opened databases have an motion (you may swipe left on a database merchandise to disclose it) to shut the database. This motion simply units the password of a database to nil, successfully closing it. As soon as the database is closed, the UI ought to replace: database icon ought to change to locked, and when you now choose the database, it ought to go to the display screen saying “Closed!”.

That is the issue. If you happen to attempt to shut the take a look at database from the instance above, the icon appropriately modifications to locked; however you continue to go to the “Opened!” display screen if you choose the take a look at databse after closing it. So the icon updates appropriately, however the NavigationLink‘s vacation spot doesn’t.

You possibly can check out the instance above to raised perceive the issue.

The fascinating level is: if I extract the code from NavigationLink‘s vacation spot right into a separate view, and go the binding to it, the issue can be fastened.

First create a view containing the whole lot we had in NavigationLink‘s vacation spot argument:

struct Vacation spot: View {
    @Binding var database: Database
    var physique: some View {
        VStack {
            if database.isOpen {
                Textual content("Opened!")
            } else {
                Textual content("Closed!")
            }
        }
    }
}

then, use it in NavigationLinks vacation spot, like this:

NavigationLink(
    vacation spot: {
        Vacation spot(database: $database)
    },
...

This fixes the whole lot: when you shut take a look at, it goes to right display screen (the one saying “Closed!”). However why? Why does utilizing a separate view work, however having the code in place would not? I am new to SwiftUI and iOS growth, and I do not perceive this. Should not the vacation spot of NavigationLink replace if database.isOpen has modified? Why would not SwiftUI choose up the modifications made to database.isOpen?

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments