基于 sheet 视图呈现 2 个选择的 SwiftUI 导航决策

SwiftUI navigation decision based on a sheet view presenting 2 choice

我正在展示一个“向导”,它将检测 BLE 设备,如果它是正确的,最后一个视图将询问我们是要注册还是跳过。

编辑:{ 查看顺序是:MainView 在 fullScreenCover 中呈现第一个信息视图,通知如何检测 BLE 设备然后这个视图推送第二个视图,其中包含最近的 BLE 设备上的一些信息,正是在这个视图中,我们有我所在的分支呈现 sheet 询问用户是否要继续并注册 BLE 设备或跳过。

So MAIN > INFOView -> BLE detection (> Register or skip ? RegisterView : Destack to main) }

我看到最后一个视图显示为 sheet 它有 2 个按钮,提到的第一个是“注册”,另一个是“跳过”。如果用户按下注册键,那么我们将关闭 sheet 并导航到正在收集个人信息以注册 BLE 设备的视图。另一方面,如果用户选择跳过,则向导需要取消堆叠回到主视图。

通常在 UIKit 中,如果选择了跳过,我只会让一个代表通知我选择。我会调用 pop 到根视图控制器,否则,如果选择了注册选项,我会关闭 sheet 视图,然后导航到另一个最终视图并让用户注册。

在 SwiftUI 中,我不知道如何处理那个导航叉。我尝试使用 PassthroughSubject,但后来我必须将 PassthroughSubject var 设置为状态 var,最后,我只是没有收到来自发送选择的回电。

尝试绑定然后希望创建一个 onReceive,但后来它要求一个发布者并且为此创建一个发布者感觉是错误的。

我想知道在 .swiftUI 中处理这个问题的最佳方法是什么?

编辑: 这是显示 BLE 设备(智能自行车)信息的视图的代码(更新了来自@Predrag Samardzic 的重播),并首先推送一个请求,了解用户是否想要注册,如果是如果没有关闭整个堆栈,请推送该注册屏幕。

struct A18BikeDiscoveryView: View {
@EnvironmentObject var bleManager: ArgonBLEManager
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
private let shouldShowRegistration = CurrentValueSubject<Bool, Never>(false)
    @State var isSheetPresented = false
    @State var isRegistrationPresented = false

var body: some View {
    VStack{
        NavigationLink(
            destination: A18RegistrationQuestionairy(QuestionairyViewModel()),
            isActive: $isRegistrationPresented
        ) {
            EmptyView()
        }
        A18ImageTextBanner(text: NSLocalizedString("bike_discovery_view_title", comment: ""))
            .padding(.bottom, 35)
            .navigationBarBackButtonHidden(true)
        if let value = bleManager.model?.bikeInfo?.bikeModel{
            Text(value)
                .fontWeight(.bold)
                .scaledFont(.largeTitle)
        }
        
        Image("subitoBike")
            .resizable()
            .frame(minWidth: 0334, idealWidth: 334, maxWidth: .infinity, minHeight: 223, idealHeight: 223, maxHeight: .infinity, alignment: .center)
            .aspectRatio(contentMode: .fit)
            .padding(.bottom, 10)
        
        Divider()
        VStack(alignment: .leading){
            HStack{
                Text("bike_discovery_view_year_created")
                if let v = bleManager.model?.bikeInfo?.year{
                    Text(v)
                }
            }
            HStack{
                Text("bike_discovery_view_model_size")
                Text("\(getSizeFromSerial())")
            }
            HStack{
                Text("bike_discovery_view_bike_serial_number")
                if let v = bleManager.model?.bikeInfo?.bikeSerialNumber {
                    Text(v)
                }
                
            }
        }
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 66, alignment: .leading)
        .padding(.horizontal, 40)
        Divider()
            .padding(.bottom, 30)
        Button(action: {
            isSheetPresented = true
        }, label: {
            Text("bike_discovery_view_bike_pairing_button_title")
                .fontWeight(.bold)
                .foregroundColor(.white)
        })
        .buttonStyle(A18RoundButtonStyle(bgColor: .red))
        .padding(.horizontal)
        .sheet(
            isPresented: $isSheetPresented,
            onDismiss: {
                if shouldShowRegistration.value {
                    isRegistrationPresented = true
                }},
            content: {
                A18BikeParingSelection(shouldShowRegistration: shouldShowRegistration)
            })
        .onReceive(shouldShowRegistration) { shouldShowRegistration in
            isSheetPresented = false
            
        }
        
        Button(action: {
            bleManager.disconect()
            self.presentationMode.wrappedValue.dismiss()
        }, label: {
            Text("bike_discovery_view_bike_pairing_cancel_button_title")
                .fontWeight(.bold)
                .foregroundColor(Color("grey55"))
        })
        .padding()
        Spacer()
    }
    .navigationBarColor(backgroundColor: .white, tintColor: .black)
    .navigationBarTitleDisplayMode(.inline)
}

func getSizeFromSerial() -> String {
    if let serial = bleManager.model?.bikeInfo?.bikeSerialNumber {
        if serial.contains("XXS"){
            return "XXS"
        }else if serial.contains("XSM") {
            return "XS"
        }else if serial.contains("SML"){
            return "S"
        }else if serial.contains("MED"){
            return "M"
        }else if serial.contains("LAR"){
            return "L"
        }
    }
    
    return "N/A"
}

}

这是一种可能的解决方案 - 使用 CurrentValueSubject 以触发关闭并保留有关在显示的屏幕上所做的选择的信息。然后,如果需要注册,则在 sheet 被关闭时触发它。

    struct MainView: View {
    private let shouldShowRegistration = CurrentValueSubject<Bool, Never>(false)
    @State var isSheetPresented = false
    @State var isRegistrationPresented = false
    
    var body: some View {
        VStack {
            // this part is if you want to push registration screen, you will need to have MainView inside NavigationView for it
            NavigationLink(
                destination: RegistrationView(),
                isActive: $isRegistrationPresented
            ) {
                EmptyView()
            }
            // ----------------------------------------------------
            Button {
                isSheetPresented = true
            } label: {
                Text("Present sheet")
            }
            .sheet(
                isPresented: $isSheetPresented,
                onDismiss: {
                if shouldShowRegistration.value {
                    isRegistrationPresented = true
                }},
                content: {
                ChoiceView(shouldShowRegistration: shouldShowRegistration)
            })
            .onReceive(shouldShowRegistration) { shouldShowRegistration in
                isSheetPresented = false
            }
            // this part is if you want to present registration screen as sheet
            //        .sheet(
            //            isPresented: $isRegistrationPresented,
            //            content: {
            //           RegistrationView()
            //        })
        }
    }
}

    struct ChoiceView: View {
    let shouldShowRegistration: CurrentValueSubject<Bool, Never>
    
    var body: some View {
        VStack{
            Button {
                shouldShowRegistration.send(false)
            } label: {
                Text("Dismiss")
            }
            
            Button {
                shouldShowRegistration.send(true)
            } label: {
                Text("Register")
            }
        }
    }
}

    struct RegistrationView: View {
        var body: some View {
            Text("Registration")
        }
    }