除了导航到另一个视图之外,NavigationLink 是否可以执行操作?

Is it possible for a NavigationLink to perform an action in addition to navigating to another view?

我正在尝试创建一个按钮,它不仅可以导航到另一个视图,而且可以同时 运行 一个功能。我尝试将 NavigationLink 和 Button 都嵌入到 Stack 中,但我只能单击 Button。

ZStack {
    NavigationLink(destination: TradeView(trade: trade)) {
        TradeButton()
    }
    Button(action: {
        print("Hello world!") //this is the only thing that runs
    }) {
        TradeButton()
    }
}

您可以使用 .simultaneousGesture 来做到这一点。 NavigationLink 将导航并同时执行与您想要的完全相同的操作:

NavigationLink(destination: TradeView(trade: trade)) {
                        Text("Trade View Link")
                    }.simultaneousGesture(TapGesture().onEnded{
                    print("Hello world!")
                })

使用 NavigationLink(_:destination:tag:selection:) 初始化器并将模型的 属性 作为 selection 参数传递。因为它是双向绑定,你可以为这个 属性 定义 didset 观察者,然后在那里调用你的函数。

struct ContentView: View {
    @EnvironmentObject var navigationModel: NavigationModel

    var body: some View {
        NavigationView {
            List(0 ..< 10, id: \.self) { row in
                NavigationLink(destination: DetailView(id: row),
                               tag: row,
                               selection: self.$navigationModel.linkSelection) {
                    Text("Link \(row)")
                }
            }
        }
    }
}

struct DetailView: View {
    var id: Int;

    var body: some View {
       Text("DetailView\(id)")
    }
}

class NavigationModel: ObservableObject {
    @Published var linkSelection: Int? = nil {
        didSet {
            if let linkSelection = linkSelection {
                // action
                print("selected: \(String(describing: linkSelection))")
            }
        }
    }
}

在此示例中,您需要将模型作为环境对象传递给 ContentView:

ContentView().environmentObject(NavigationModel())

在 SceneDelegate 和 SwiftUI 预览中。

模型符合 ObservableObject 协议,属性 必须有一个@Published 属性。

(它在列表中工作)

您可以使用 NavigationLink(destination:isActive:label:)。使用绑定上的 setter 可以知道何时点击 link。我注意到可以在内容区域之外点击 NavigationLink,并且这种方法也可以捕获这些点击。

struct Sidebar: View {
    @State var isTapped = false

    var body: some View {
        NavigationLink(destination: ViewToPresent(),
                       isActive: Binding<Bool>(get: { isTapped },
                                               set: { isTapped = [=10=]; print("Tapped") }),
                       label: { Text("Link") })
    }
}

struct ViewToPresent: View {
    var body: some View {
        print("View Presented")
        return Text("View Presented")
    }
}

我唯一注意到的是 setter 触发了三次,其中一次是在它出现之后。这是输出:

Tapped
Tapped
View Presented
Tapped

NavigationLink + isActive + onChange(of:)

// part 1
@State private var isPushed = false

// part 2
NavigationLink(destination: EmptyView(), isActive: $isPushed, label: {
    Text("")
})

// part 3
.onChange(of: isPushed) { (newValue) in
    if newValue {
        // do what you want
    }
}

这适用于我的 atm:

@State private var isActive = false

NavigationLink(destination: MyView(), isActive: $isActive) {
    Button {
        // run your code
        
        // then set
        isActive = true

    } label: {
        Text("My Link")
    }
}

我也刚用过:

NavigationLink(destination: View()....) { 
    Text("Demo")
}.task { do your stuff here }

iOS 15.3 部署目标。