I've read a lot here about navigation in SwiftUI and tried a couple of things, but nothing is working as desired.
Basically, I have a view with a list of workouts and you can show a single workout by clicking on a row. This works as expected using NavigationView together with NavigationLink.
Now, I want a button on the detail view to start the workout. This should open a view with a timer. The view should be presented with the same animation as the detail view did and also show the name of the workout in the navigation bar with a back button.
I could implement this with a NavigationLink view on the details page, but the link always appears as a full width row with the arrow on the right side. I'd like this to be a button instead, but the NavigationLink seems to be resistant against styling.
struct WorkoutDetail: View {
var workout: Workout
var body: some View {
VStack {
NavigationLink(destination: TimerView()) {
Text("Starten")
}.navigationBarTitle(Text(workout.title))
}
}
}
struct WorkoutList: View {
var workoutCollection: WorkoutCollection
var body: some View {
NavigationView {
List(workoutCollection.workouts) { workout in
NavigationLink(destination: WorkoutDetail(workout: workout)) {
WorkoutRow(workout: workout)
}
}.navigationBarTitle(Text("Workouts"))
}
}
}
Updated: Here's a screenshot to illustrate what I mean:
SwiftUI's button is similar to UIButton , except it's more flexible in terms of what content it shows and it uses a closure for its action rather than the old target/action system. To create a button with a string title you would start with code like this: Button("Button title") { print("Button tapped!") }
You don't need to wrap your view inside the NavigationLink
to make it trigger the navigation when pressed.
We can bind a property with our NavigationLink
and whenever we change that property our navigation will trigger irrespective of what action is performed. For example:
struct SwiftUI: View {
@State private var action: Int? = 0
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("Destination_1"), tag: 1, selection: $action) {
EmptyView()
}
NavigationLink(destination: Text("Destination_2"), tag: 2, selection: $action) {
EmptyView()
}
Text("Your Custom View 1")
.onTapGesture {
//perform some tasks if needed before opening Destination view
self.action = 1
}
Text("Your Custom View 2")
.onTapGesture {
//perform some tasks if needed before opening Destination view
self.action = 2
}
}
}
}
}
Whenever you change the value of your Bindable
property (i.e. action) NavigationLink
will compare the pre-defined value of its tag
with the binded property action
, if both are equal navigation takes place.
Hence you can create your views any way you want and can trigger navigation from any view regardless of any action, just play with the property binded with NavigationLink
.
Thanks, hope it helps.
I think that the accurate way to do it is using buttonStyle
, for example
NavigationLink(destination: WorkoutDetail(workout: workout)) {
WorkoutRow(workout: workout)
}
.buttonStyle(ButtonStyle3D(background: Color.yellow))
I've been playing around with this for a few days myself. I think this is what you're looking for.
struct WorkoutDetail: View {
var workout: Workout
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: TimerView()) {
ButtonView()
}.navigationBarTitle(Text(workout.title))
}
}
}
And create a View you want to show in the NavigationLink
struct ButtonView: View {
var body: some View {
Text("Starten")
.frame(width: 200, height: 100, alignment: .center)
.background(Color.yellow)
.foregroundColor(Color.red)
}
Note: Anything above the NavigationView appears to show up on all pages in the Navigation, making the size of the NavigationView Smaller in the links.
Use the NavigationLink inside the button's label.
Button(action: {
print("Floating Button Click")
}, label: {
NavigationLink(destination: AddItemView()) {
Text("Open View")
}
})
Very similar with Pankaj Bhalala's solution but removed ZStack:
struct ContentView: View {
@State var selectedTag: String?
var body: some View {
Button(action: {
self.selectedTag = "xx"
}, label: {
Image(systemName: "plus")
})
.background(
NavigationLink(
destination: Text("XX"),
tag: "xx",
selection: $selectedTag,
label: { EmptyView() }
)
)
}
}
Wait for a more concise solution without state. How about you?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With