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
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.
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