Swiftui - Make Toolbar's Navigationlink Use Detail View

SwiftUI - Make toolbar's NavigationLink use detail view

Universal solution

As you might know, NavigationLink doesn't work well when placed in a toolbar. What is suggested here - is to place a Button into a toolbar and use a hidden NavigationLink somewhere in the code. The button tells the link to open the detail view and the link does the action. Here is your code adjusted with this suggestion:

struct ContentView: View {
/// A state that tracks whether the link in the toolbar should be opened
@State var toolbarLinkSelected = false

var body: some View {
NavigationView {
List {
NavigationLink(destination: Text("Opened from sidebar")) {
Text("Open from sidebar")
}
}.listStyle(SidebarListStyle())
.navigationTitle("Sidebar")
.toolbar {
ToolbarItem {
Button(action: { toolbarLinkSelected = true }) {
Text("Open from toolbar")
}
}
}
.background(
NavigationLink(
destination: Text("Opened from toolbar"),
isActive: $toolbarLinkSelected
) {
EmptyView()
}.hidden() // The link is not visible to user
)
Text("Detail pane")
}.navigationViewStyle(DoubleColumnNavigationViewStyle())
}
}

iOS / iPadOS only solution

Also, if you only need to support iOS and iPadOS, you can use an older way of implementing it using navigationBarItems. This won't work on macOS as this modifier is not available but works well for iOS/iPadOS. Here is the example:

struct ContentView: View {
var body: some View {
NavigationView {
List {
NavigationLink(destination: Text("Opened from sidebar")) {
Text("Open from sidebar")
}
}.listStyle(SidebarListStyle())
.navigationTitle("Sidebar")
.navigationBarItems(trailing: NavigationLink(destination: Text("Opened from toolbar")) {
Text("Open from toolbar")
})
Text("Detail pane")
}.navigationViewStyle(DoubleColumnNavigationViewStyle())
}
}

Using same View for different data via NavigationLink - SwiftUI

A way to have only one view that can reload is to have a dynamic way to define its contents. One might use an enum to save the state of the survey :

class ListModel: ObservableObject {
// The enum is the list of tests
enum Choosing {
case male
case female
case color
// Define each test title
var title: String {
switch self {
case .male:
return "Male Names"
case .female:
return "Female Names"
case .color:
return "Color Names"
}
}
// define each test possible values
var items: [String] {
switch self {
case .male:
return ["Todd", "Liam", "Noah", "Oliver", "James", "William"]
case .female:
return ["Jessica", "Monica", "Stephanie"]
case .color:
return ["Pink", "Blue", "Yellow", "Green"]
}
}
// choosing next test
var next: Choosing? {
switch self {
case .male:
return .female
case .female:
return .color
case .color:
return nil
}
}
}

@Published var choosedItems: [Choosing:[String]] = [.male:[], .female:[], .color:[]]
}

struct StartView: View {
@StateObject var listModel = ListModel()

var body: some View {
NavigationView{
NavigationLink(destination: {
// Just give model and first test
OwnListView(listModel: listModel,
choosing: .male)
}, label: {
Text("Start")
.bold()
})
}
}
}

The common view :

struct OwnListView: View {
//ListModel
@ObservedObject var listModel: ListModel

var choosing: ListModel.Choosing
// Use enum var to get title and items
var title: String {
choosing.title
}
var items: [String] {
choosing.items
}

var body: some View {
VStack{
Text(title)
.font(.largeTitle)
.bold()

ForEach(choosing.items, id: \.self){ item in
// Use the current test result
let alreadyInList: Bool = listModel.choosedItems[choosing]?.contains(where: { $0 == item }) ?? false

Button(action: {
if alreadyInList {
listModel.choosedItems[choosing]?.removeAll(where: { $0 == item })
} else {
listModel.choosedItems[choosing]?.append(item)
}
}, label: {
//Can be an own View, but for simplicity
ZStack{
Rectangle()
.fill(alreadyInList ? .black : .purple)
.frame(width: 250, height: 50)

Text(item)
.bold()
.foregroundColor(.white)
}
})
}

Spacer()

// if still somthing test next
if let next = choosing.next {
NavigationLink(destination: {
OwnListView(listModel: listModel,
choosing: next)
}, label: {
Text("Continue")
})
} else {
// Here you can have a button to navigation link to go to end of survey
Text("Finish")
}

Spacer()

}.navigationBarTitleDisplayMode(.inline)
}
}

Note: The enum and title and values could comes from external json file to make it more generic. Here was just a way to do it.
To complete survey, just complete the enum definitions.

SwiftUI auto navigates to detail view after saving and dismissing the view

The solution for this is to make the navigation be based on a binding state.

NavigationLink(
destination: ExchangeItemSelectedView(exchange: observer),
tag: exchange.id,
selection: $exchangeSelection
) {
Text("Tap Me")
}

then rather than using @State to store exchangeSelection use @SceneStorage this will let you access the binding from anywhere within your app, in the code that creates the new item it should then dispatch async to update the selection value to the new item ID.

Force one NavigationLink to the detail view in SwiftUI

Hey, I have seen your screenshots. I Think you are using NavigationView 2 times there in Settings. Please remove any one of them, It should work fine!



Related Topics



Leave a reply



Submit