SwiftUI:检测NavigationView返回按钮的按下

23

SwiftUI 中,当我处于代码中的 DetailView1 时,我找不到一种方法来检测用户点击导航视图的默认返回按钮。

struct RootView: View {
    @State private var showDetails: Bool = false
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: DetailView1(), isActive: $showDetails) {
                    Text("show DetailView1")
                }
            }
            .navigationBarTitle("RootView")
        }
    }
}

struct DetailView1: View {
    @State private var showDetails: Bool = false
    var body: some View {
        NavigationLink(destination: DetailView2(), isActive: $showDetails) {
            Text("show DetailView2")
        }
        .navigationBarTitle("DetailView1")
    }
}

struct DetailView2: View {
    var body: some View {
        Text("")
            .navigationBarTitle("DetailView2")
    }
}

使用 .onDisappear 并不能解决问题,因为它的闭包在视图被弹出或者新视图被推入时被调用。


在检测到后,您想要做什么?当用户点击“返回”按钮时,您的DetailView1视图将被关闭,并且您的showDetails状态变量将重置为false - 也许这就是您正在寻找的关键? - markiv
1
showDetails.onDisappear之后才被改为false,而不是之前,因此不能用于早期返回按钮的点击检测。以防万一。 - Asperi
4个回答

19

最快的解决方法是创建一个自定义的返回按钮,因为目前该框架没有这个功能。

struct DetailView : View {

    @Environment(\.presentationMode) var mode: Binding<PresentationMode>

    var body : some View {
        Text("Detail View")
            .navigationBarBackButtonHidden(true)
            .navigationBarItems(leading: Button(action : {
                self.mode.wrappedValue.dismiss()
            }){
                Image(systemName: "arrow.left")
            })
    }
}

1
我确认这种方式。当用户按下“返回”按钮时,我想保存一些表单值。这种方法很好用。我只想稍微更新一下设计。使用“chevron.left”而不是“arrow.left”符号。HStack { Image(systemName:"chevron.left") Text("返回").font(.body) } - Oleksii Radetskyi
这是最好的解决方案,因为您可以在关闭视图之前执行代码。 - Adam
8
如果您正在使用此解决方案,您可能还希望确保如果用户通过滑动返回而不是点击“返回”按钮,则您的操作也能运行。 - nishanthshanmugham
1
NB. navigationBarItems(leading:) 在 macOS 上不受支持。 - Nitesh

9
一旦按下返回按钮,视图将isPresented设置为false,因此您可以在该值上使用观察者来触发代码,当按下返回按钮时。假设此视图在导航控制器中呈现:
struct MyView: View {
    @Environment(\.isPresented) var isPresented

    var body: some View {
        Rectangle().onChange(of: isPresented) { newValue in
            if !newValue {
                print("detail view is dismissed")
            }
        }
    }
}
    

4
仅适用于 iOS 15 及以上版本。 - nikans
7
无论您将一个新视图推入堆栈,还是弹出当前视图,“isPresented”都将变为false,因此此答案仅在DetailView是导航堆栈中的最后一个视图时才有效。OP的问题明确提到他们不能使用.onDisappear,因为存在相同的问题。 - Behdad

2

观察已发布的showDetails属性的更加优美(SwiftUI-ier?)的方式:

struct RootView: View {
    class ViewModel: ObservableObject {
        @Published var showDetails = false
    }
    @ObservedObject var viewModel = ViewModel()

    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: DetailView1(), isActive: $viewModel.showDetails) {
                    Text("show DetailView1")
                }
            }
            .navigationBarTitle("RootView")
            .onReceive(self.viewModel.$showDetails) { isShowing in
                debugPrint(isShowing)
                // Maybe do something here?
            }
        }
    }
}

无论是 (1) 当我将视图弹出到根视图时,还是 (2) 当我转到 DetailView2 时,onReceive 闭包都会以 isShowing = false 被调用,因此这个解决方案并不能解决问题。 - JAHelia

1

跟进我的评论,我会对 showDetails 状态的更改做出反应。不幸的是,didSet 似乎不会触发 @State 变量。相反,我们可以使用可观察的视图模型来保存状态,这样就可以使用 didSet 拦截更改。

struct RootView: View {
    class ViewModel: ObservableObject {
        @Published var showDetails = false {
            didSet {
                debugPrint(showDetails)
                // Maybe do something here?
            }
        }
    }
    @ObservedObject var viewModel = ViewModel()

    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: DetailView1(), isActive: $viewModel.showDetails) {
                    Text("show DetailView1")
                }
            }
            .navigationBarTitle("RootView")
        }
    }
}

使用.onDisappear,如果在NavigationView中使用NavigationLink来检测按下返回按钮的事件。 - Asif Raza

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接