Home > OS >  NavigationItem in nested NavigationStack is below NavigationBar
NavigationItem in nested NavigationStack is below NavigationBar

Time:01-15

I'm making an app where you create acronyms and their meaning with SwiftUI

The flow of the app consists of having a List of Acronym, when tapping on any element it takes you to the detailView for that particular acronym, and there you can tap on an edit button which should take you to the next screen to edit the available fields

However when creating the detailView, and adding my edit button as a NavigationItem it is being placed below the NavigationBar

enter image description here

AcronymsView

import SwiftUI

struct AcronymsView: View {
    @State var isShowingDetailView = false
    
    let acronyms = [
        Acronym(short: "OMG", long: "Oh My God", userID: UUID()),
        Acronym(short: "OMG 2", long: "Oh My God 2", userID: UUID()),
        Acronym(short: "OMG 3", long: "Oh My God 3", userID: UUID())
    ]
    let user = User(name: "User", username: "Username")
    
    var body: some View {
        NavigationStack {
            List(acronyms) { acronym in
                NavigationLink(value: acronym) {
                    AcronymsListCell(acronym: acronym)
                }
            }
            .navigationDestination(for: Acronym.self) { acronym in
                AcronymDetailView(acronym: acronym, user: user, categories: [])
            }
            .listStyle(.plain)
            .toolbar {
                Label("", systemImage: "plus")
            }
            .toolbarBackground(.visible, for: .navigationBar)
            .navigationTitle("Acronyms")
            .navigationBarTitleDisplayMode(.inline)
        }
    }
}

AcronymDetailView

import SwiftUI

struct AcronymDetailView: View {
    @State var isEditing = false
    @State var acronym: Acronym
    @State var user: User
    @State var categories: [Category]
    
    var body: some View {
        NavigationView {
            Form {
                Section("Acronym") {
                    TextField("Acronym", text: $acronym.short)
                }
                
                Section("Meaning") {
                    TextField("Meaning", text: $acronym.long)
                }
                
                Section("User") {
                    TextField("User", text: $user.name)
                }
                
                Section("Categories") {
                    VStack {
                        ForEach(categories, id: \.id) {
                            Text($0.name)
                        }
                    }
                }
                
                Section {
                    Button("Add to category") {
                        print("Wololo")
                    }
                    .buttonStyle(.plain)
                }
            }
            .navigationTitle(acronym.short)
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Text("Edit")
                }
            }
        }
    }
}

Acronym

import Foundation

final class Acronym: Codable, Identifiable {
    var id: UUID?
    var short: String
    var long: String
    var user: AcronymUser
    
    init(short: String, long: String, userID: UUID) {
        self.id = UUID()
        self.short = short
        self.long = long
        let user = AcronymUser(id: userID)
        self.user = user
    }
}

extension Acronym: Hashable {
    public func hash(into hasher: inout Hasher) {
        return hasher.combine(id)
    }
    
    static func == (lhs: Acronym, rhs: Acronym) -> Bool {
        lhs.id == rhs.id && lhs.short == rhs.short && lhs.long == rhs.long && lhs.user == rhs.user
    }
}

final class AcronymUser: Codable, Identifiable {
    var id: UUID
    
    init(id: UUID) {
        self.id = id
    }
}

extension AcronymUser: Hashable {
    public func hash(into hasher: inout Hasher) {
        return hasher.combine(id)
    }
    
    static func == (lhs: AcronymUser, rhs: AcronymUser) -> Bool {
        lhs.id == rhs.id
    }
}

User

import Foundation

final class User: Codable {
    var id: UUID?
    var name: String
    var username: String
    
    init(name: String, username: String) {
        self.name = name
        self.username = username
    }
}

Something funny (odd) that happens when I modify NavigationView to NavigationStack in AcronymDetailView is that it moves to the detailView when I tap on a List element, but then it goes back immediately and then I can see this error in the console:

A NavigationLink is presenting a value of type “Acronym” but there is no matching navigationDestination declaration visible from the location of the link. The link cannot be activated.

enter image description here

I'm not sure if it's related, but I thought it was worth mentioning it here just in case, and if you look closely to the gif, it looks like the button is in the right place

CodePudding user response:

After a bit of trial and error and thinking of this logically, I don't really need another NavigationStack inside my AcronymDetailView

Also I replaced:

.toolbar {
    ToolbarItem(placement: .navigationBarTrailing) {
        Text("Edit")
    }
}

For

.toolbar {
    NavigationLink {
        AcronymEditView()
    } label: {
        Text("Edit")
    }
}

This way the code looks like this now:

import SwiftUI

struct AcronymDetailView: View {
    @State var isEditing = false
    @State var acronym: Acronym
    @State var user: User
    @State var categories: [Category]
    
    var body: some View {
        Form {
            Section("Acronym") {
                TextField("Acronym", text: $acronym.short)
            }
            
            Section("Meaning") {
                TextField("Meaning", text: $acronym.long)
            }
            
            Section("User") {
                TextField("User", text: $user.name)
            }
            
            Section("Categories") {
                VStack {
                    ForEach(categories, id: \.id) {
                        Text($0.name)
                    }
                }
            }
            
            Section {
                Button("Add to category") {
                    print("Wololo")
                }
                .buttonStyle(.plain)
            }
        }
        .navigationTitle(acronym.short)
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            NavigationLink {
                AcronymEditView()
            } label: {
                Text("Edit")
            }

        }
    }
}

And now, everything works as it should

enter image description here

  • Related