I have List
like below,
struct ListItemView: View {
let vm: ListItemViewModel
var callHandler: ((ListItemViewModel) -> Void)?
var textHandler: ((ListItemViewModel) -> Void)?
HStack {
Text("Click Me")
}
}
.swipeActions(allowsFullSwipe: false) {
Button {
textAction()
} label: {
Label("Go", systemImage: "text.bubble.fill")
}
.tint(.green)
}
private extension PhoneItemView {
var callAction: () -> Void {
return {
callHandler?(vm)
}
}
I'm trying to figure out how to trigger a NavigationLink
when swipe action is pressed. My ContentView
looks like this.
List{
ForEach(viewModel.data) { item in
ZStack {
NavigationLink(destination: CallDetailView())
.{EmptyView()}.opacity(0.0)
PhoneItemView(vm: log, callHandler: { _ in
print("swipe action tapped")
NavigationLink(destination: DetailView())
{ EmptyView() }
})
}
}
}
But, even-though the log 'swipe action tapped' gets printed. DetailView
is not loaded. Also, there's an warning on the NavigationLink
of DetailView
.
Result of 'NavigationLink<Label, Destination>' initializer is unused
Any help would be much appreciated!
A NavigationLink
is a View
that triggers a navigation action when tapped. It isn't just a Void
closure that you can execute a demand. You need to add the NavigationLink
to your View hierarchy, you can't create it inside a void closure.
If you want to trigger navigation conditionally, you need to be using the NavigationLink
initialiser which takes an isActive
Binding
instead and set the Binding
to true
when you want to trigger the navigation link.
@State private var navigateToDetailView: Bool = false
List {
ForEach(viewModel.data) { item in
ZStack {
NavigationLink(destination: CallDetailView())
.{EmptyView()}.opacity(0.0)
PhoneItemView(vm: log, callHandler: { _ in
print("swipe action tapped")
// Set the binding to true to trigger navigation
navigateToDetailView = true
})
}
}
}
// Add the NavigationLink to your view
.background(
NavigationLink(
destination: DetailView(),
isActive: $navigateToDetailView
)
{
EmptyView()
}
)
If you want to be able to handle more than one destination, you just need to modify your @State
to be an optional enum, representing your destination view. Your isActive
Binding
also has to change so that it is active in case your enum
has a non-nil value.
struct ViewWithMultipleConditionalDestinations: View {
private enum Destination {
case one
case two
}
@State private var destination: Destination?
var body: some View {
NavigationView {
VStack {
Button("Destination one") {
destination = .one
}
Button("Destination two") {
destination = .two
}
}
.background(
NavigationLink(
destination: destinationView,
isActive: Binding(get: { destination != nil }, set: { if !$0 { destination = nil } }),
label: { EmptyView() }
)
)
}
}
@ViewBuilder
private var destinationView: some View {
switch destination {
case .none:
EmptyView()
case .one:
Text("One")
case .two:
Text("Two")
}
}
}