SwiftUI:单击NavigationLink时如何做其他工作?

时间:2020-01-23 00:43:17

标签: swiftui

现在我有一个导航链接

NavigationView {
     NavigationLink(destination: AnotherView())) {
          Text("Click Here")
     }
}
    .navigationBarTitle("SwiftUI")

我想发生的事情是,当我单击导航链接时,我希望实例化一个对象并将其放入数据库中。

换句话说,我的问题是:除了使用NavigationLink时仅推送新视图之外,您还如何执行工作?

3 个答案:

答案 0 :(得分:2)

对于您描述的情况,这是最简单的方法

NavigationView {
     NavigationLink("Click Here", destination: 
         AnotherView()
             .onAppear {
                 // any action having access to current view context
             })
}

答案 1 :(得分:1)

SwiftUI并没有提供简便的方法,但是确实提供了一种方法。您应该使用Button,其动作会触发NavigationLink

问题在于,执行此操作的代码既繁琐又丑陋。因此,我编写了一个控件,将其包装起来类似于ButtonNavigationLink的爱子。

struct ActionNavigationLink<Destination: View, Content: View>: View {
    private let destination: Destination
    private let content: Content
    private let action: (NavigationToken) -> ()

    @State private var continueToDestination: Bool = false

    init(
        destination: Destination,
        action: @escaping (NavigationToken) -> (),
        @ViewBuilder content: () -> Content
    ) {
        self.destination = destination
        self.action = action
        self.content = content()
    }

    var body: some View {
        Button(action: buttonAction, label: { content })
            .background(
                NavigationLink(
                    destination: destination,
                    isActive: $continueToDestination,
                    label: { EmptyView() }
                )
            )
    }

    private func buttonAction() {
        let token = NavigationToken {
            self.continueToDestination = true
        }

        action(token)
    }
}

NavigationToken是一个非常简单的结构,可以执行操作。该动作定义了一旦触发导航将如何执行。

struct NavigationToken {
    private let navigationAction: () -> ()

    fileprivate init(_ action: @escaping () -> ()) {
        self.navigationAction = action
    }

    func navigate() {
        navigationAction()
    }
}

此令牌将传递给您使用此控件提供的操作,以便在实际执行导航时完全控制您。因此,如果您需要执行一些API调用并且仅在回调返回后进行导航,那么您完全可以做到这一点。

在这里,我通过将导航延迟3秒来模拟API调用。

ActionNavigationLink(
    destination: EmptyView(),
    action: { token in
        DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) {
            token.navigate()
        }
    }
) {
    Text("Delayed Navigation")
}

完整来源可在this gist上找到。

答案 2 :(得分:0)

NavigationLink有一些接受Binding的重载。您通过绑定初始化它,并在setter中产生副作用,或者类似地,您可以使用具有某些@Published属性的模型,并订阅其内部发布者。这是一个同时执行这两个操作的示例:

class Model: ObservableObject {
  @Published var isLinkActive = false
  var subscriptions = Set<AnyCancellable>()
  init() {
    $isLinkActive
      .sink { _ in
        print("I'm a side effect in the model")
      }
    .store(in: &subscriptions)
  }
}

struct ContentView: View {
  @ObservedObject var model: Model
  let isLinkActive: Binding<Bool>
  init(model: Model = .init()) {
    self.model = model
    self.isLinkActive = Binding<Bool>(
      get: { [model] in model.isLinkActive},
      set: { [model] in
        print("I'm a side effect in the binding!")
        model.isLinkActive = $0
    })
  }
  var body: some View {
    NavigationView {
      NavigationLink("hello", destination: Color.red, isActive: isLinkActive)
    }
  }
}