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
Swift 2 Mkmapviewdelegate Rendererforoverlay Optionality
How to Extend Float3 or Any Other Built-In Type to Conform to the Codable Protocol
How to Pass a Reference to a Boolean Rather Than Its Value
Xcode: Using Custom Fonts Inside Dynamic Framework
Generate Random Number of Certain Amount of Digits
What's the Advantage of #Selector Update in Swift 2.3
How to Encode and Decode the Closures
Images Inaccessible from Asset Catalog in a Swiftui Framework
Exc_Bad_Instruction Happens When Using Dispatch_Get_Global_Queue on iOS 7(Swift)
Uiimagepickercontroller Navigation Bar Tint Color Not Working with iOS 13
How to Declare, Create, and Use Method Pointers in Swift
Cannot Call Value of Non-Function Type 'Ciimage'
How to Draw Dashed Line in Arkit (Scenekit) Like in the Measure App
Is There *Any* Situation Under Which "For _ in [1,2,3]" Will Not Loop at All
Swift Error Comparing Two Arrays of Optionals
"Message from Debugger: Unable to Attach" When Running Tests on Osx App
How to Open a Url in Safari Even If My Default Browser Is Not Safari Using Swift in MACos