基于显示 2 选择的工作表视图的 SwiftUI 导航决策

Posted

技术标签:

【中文标题】基于显示 2 选择的工作表视图的 SwiftUI 导航决策【英文标题】:SwiftUI navigation decision based on a sheet view presenting 2 choice 【发布时间】:2021-07-02 19:54:32 【问题描述】:

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

编辑: 视图顺序是: MainView 在 fullScreenCover 中显示第一个信息视图,通知如何检测 BLE 设备,然后这个推送第二个视图,其中包含最近的 BLE 设备上的一些信息,正是在这个视图中,我们有我所在的分支出示一张表格,询问用户是要继续注册 BLE 设备还是跳过。

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

我将最后一个视图显示为一张有 2 个按钮的表单,上面提到的第一个显示“注册”,另一个显示“跳过”。如果用户按下注册,那么我们关闭工作表并导航到正在收集个人信息以注册 BLE 设备的视图。另一方面,如果用户选择跳过,那么向导需要解栈回到主视图。

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

在 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"

【问题讨论】:

【参考方案1】:

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

    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")
        
    

【讨论】:

感谢您的帖子。它确实可以展示收集用户信息的视图。但是需要解除堆栈的部分不需要。我很难受。可能我仍然对 UIKit 框架的想法太多了。我尝试使用 PresentationMode 将视图堆栈放回主视图,但这不起作用。我会用一些代码更新这篇文章。 我最终为首先出现的视图复制了相同的架构。

以上是关于基于显示 2 选择的工作表视图的 SwiftUI 导航决策的主要内容,如果未能解决你的问题,请参考以下文章

从 SwiftUI 中的工作表更新列表视图中的行

使用 SwiftUI 时尝试在视图中显示工作表时应用程序崩溃

SwiftUI:从工作表导航到新视图

所有视图的 SwiftUI 视图,包括工作表视图

SwiftUI - 日期选择器未正确显示

UIKit 中等效的 SwiftUI 工作表视图是啥?