SwiftUI:如何实现Navigation Link旁按钮与导航链接的独立交互
Hey there! Let's fix your issues step by step—first the annoying navigation/button conflict, then clean up that data structure to make your code more robust.
Step 1: Ditch the Dual Arrays—Use an Identifiable Model
Your current setup with two separate arrays (fruits and fruitIds) is error-prone because indices can get out of sync when you delete items. Instead, create a simple Identifiable struct to hold your fruit data. This makes ForEach work seamlessly without needing to track separate IDs:
import SwiftUI struct Fruit: Identifiable { let id = UUID() // Auto-generates a unique ID for each fruit let name: String } struct ContentView: View { @State private var fruits: [Fruit] = [ Fruit(name: "apple"), Fruit(name: "orange"), Fruit(name: "banana"), Fruit(name: "peach") ] var body: some View { NavigationStack { // Use NavigationStack instead of deprecated NavigationView (iOS 16+) List { // Updated list code coming next } .navigationTitle("Fruits") } } }
Step 2: Fix the Tap Conflict Between Button & NavigationLink
The problem you're seeing is because SwiftUI's gesture bubbling causes the NavigationLink to trigger even when you tap the button. To fix this, add a .highPriorityGesture() modifier to your button—this tells SwiftUI to prioritize the button's tap over the NavigationLink's tap.
Here's the updated list code:
List { ForEach(fruits) { fruit in HStack { Button { // Safely remove the fruit by its unique ID fruits.removeAll { $0.id == fruit.id } } label: { Image(systemName: "circle") .imageScale(.large) .foregroundColor(.accentColor) } .highPriorityGesture(TapGesture()) // Critical: Stops taps from bubbling to NavigationLink NavigationLink(destination: FruitDetailView(fruitName: fruit.name)) { Text(fruit.name) } } } }
Bonus: Create a Proper Detail View
Instead of using a plain Text view for the destination, it's cleaner to make a dedicated detail view:
struct FruitDetailView: View { let fruitName: String var body: some View { Text("Details for \(fruitName)") .navigationTitle(fruitName) } }
Why This Works
- Identifiable Model: Eliminates index mismatches that come with tracking separate ID arrays. Each fruit has its own unique, unchanging ID, so deleting is safe and straightforward.
- High Priority Gesture: Ensures that tapping the delete button only triggers the delete action, not the navigation. Tapping the rest of the row (the
NavigationLinkarea) will still take you to the detail view as expected. - Modern Navigation: Used
NavigationStackinstead of the deprecatedNavigationView(for iOS 16+; if you need to support older versions, you can switch back toNavigationView).
Quick Note for iOS <16
If you're targeting iOS 15 or earlier, replace NavigationStack with NavigationView and wrap your list in it as you did before. The rest of the code will work the same.
内容的提问来源于stack exchange,提问作者Malachy Fernandez

