Home > OS >  SwiftUI macOS NavigationView - onChange(of: Bool) action tried to update multiple times per frame
SwiftUI macOS NavigationView - onChange(of: Bool) action tried to update multiple times per frame

Time:03-06

I'm seeing onChange(of: Bool) action tried to update multiple times per frame warnings when clicking on NavigationLinks in the sidebar for a SwiftUI macOS App.

Here's what I currently have:

import SwiftUI

@main
struct BazbarApp: App {

    @StateObject private var modelData = ModelData()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(modelData)
        }
    }
}


class ModelData: ObservableObject {
    @Published var myLinks = [URL(string: "https://google.com")!, URL(string: "https://apple.com")!, URL(string: "https://amazon.com")!]
}

struct ContentView: View {

    @EnvironmentObject var modelData: ModelData

    @State private var selected: URL?

    var body: some View {
        NavigationView {

            List(selection: $selected) {
                Section(header: Text("Bookmarks")) {

                    ForEach(modelData.myLinks, id: \.self) { url in
                        NavigationLink(destination: DetailView(selected: $selected) ) {
                            Text(url.absoluteString)
                        }
                        .tag(url)
                    }

                }
            }
            .onDeleteCommand {
                if let selected = selected {
                    modelData.myLinks.remove(at: modelData.myLinks.firstIndex(of: selected)!)
                }

                selected = nil
            }

            Text("Choose a link")
        }
    }
}

struct DetailView: View {
    @Binding var selected: URL?

    var body: some View {

        if let selected = selected {
            Text("Currently selected: \(selected)")
        }
        else {
            Text("Choose a link")
        }
    }
}

When I alternate clicking on the second and third links in the sidebar, I eventually start seeing the aforementioned warnings in my console.

Here's a gif of what I'm referring to: gif of alternating clicks on sidebar link causing warnings

Interestingly, the warning does not appear when alternating clicks between the first and second link.

Does anyone know how to fix this?

I'm using macOS 12.2.1 & Xcode 13.2.1.

Thanks in advance

CodePudding user response:

I think the issue is that both the List(selection:) and the NavigationLink are trying to update the state variable selected at once. A List(selection:) and a NavigationLink can both handle the task of navigation. The solution is to abandon one of them. You can use either to handle navigation.

Since List look good, I suggest sticking with that. The NavigationLink can then be removed. The second view under NavigationView is displayed on the right, so why not use DetailView(selected:) there. You already made the selected parameter a binding variable, so the view will update if that var changes.

struct ContentView: View {
    
    @EnvironmentObject var modelData: ModelData
    
    @State private var selected: URL?
    
    var body: some View {
        NavigationView {
            
            List(selection: $selected) {
                Section(header: Text("Bookmarks")) {
                    
                    ForEach(modelData.myLinks, id: \.self) { url in
                        Text(url.absoluteString)
                            .tag(url)
                    }
                    
                }
            }
            .onDeleteCommand {
                if let selected = selected {
                    modelData.myLinks.remove(at: modelData.myLinks.firstIndex(of: selected)!)
                }
                
                selected = nil
            }
            
            DetailView(selected: $selected)
        }
    }
}
  • Related