如何在基于 SwiftUI 的 iOS 应用中呈现连续视图?

Posted

技术标签:

【中文标题】如何在基于 SwiftUI 的 iOS 应用中呈现连续视图?【英文标题】:How can I present successive views in a SwiftUI-based iOS app? 【发布时间】:2021-01-09 18:55:07 【问题描述】:

在尝试学习 SwiftUI 时,我正在开发一个 ios 应用程序,该应用程序显示“观察会话”的列表视图,并允许用户从“新建”按钮创建新会话。它需要一个中间步骤来选择新会话所基于的配置。

我能够显示合理的会话列表和配置列表屏幕,但我处理所选配置的尝试失败了。

发送到配置列表屏幕的闭包被成功调用,打印语句正确显示配置名称就证明了这一点。但是应该呈现第三种视图类型的处理程序的其余部分无法工作(即它不呈现视图)。此外,我收到一条警告,我试图展示“未使用 'sheet(isPresented:onDismiss:content:)' 的调用结果”的新视图。我希望有人可以向我解释我做错了什么。这是在 Xcode 12.3 中,针对模拟器中的 iOS 14。下面是出现问题的 SessionListView 代码:

import SwiftUI

struct SessionsListView: View 
    @ObservedObject var dataManager: DataManager
    @State private var isPresented = false
    @State private var isObserving = false
    var body: some View 
        VStack 
            List 
                ForEach(dataManager.allSavedSessions) session in
                    NavigationLink(
                        // Navigate to a detail view
                        destination: SessionDetailView(session: session),
                        label: 
                            Text("\(session.name)")
                        )
                
            
            Spacer()
            Button("New Session") 
                isPresented = true
            
            .padding()
            .font(.headline)
            .sheet(isPresented: $isPresented) 
                // Present a configuration list view where user must select configuration to use for new session
                // Requires a closure that's called upon selection in the configuration list view, to handle the selection
                NavigationView 
                    ConfigurationsListView(dataManager: dataManager, selectionHandler:  config in
                        isPresented = false
                        isObserving = true
                        handleConfigSelection(config)
                    )
                    .navigationTitle("Configurations")
                    .navigationBarItems(trailing: Button("Cancel") 
                        isPresented = false
                    )
                
            
        
    
        
    private func handleConfigSelection(_ config: SessionConfiguration) 
        // Use the selected configuration to start an observations session
        print("Selected \(config.name). Will attempt to show sheet from \(self)")
        isPresented = false
        isObserving = true
        self.sheet(isPresented: $isObserving)  // displaying warning: "Result of call to 'sheet(isPresented:onDismiss:content:)' is unused"
            NavigationView 
                ObservationsView(configuration: config)
                    .navigationBarItems(trailing: Button(action: ) 
                        Text("Done")
                    )
            
        
    

这是我在这个模型类型的简化演示中使用的代码。

观察会话:


struct ObservationSession: Identifiable 
    let id: UUID = UUID()
    let name: String

会话配置:

import Foundation

struct ObservationSession: Identifiable 
    let id: UUID = UUID()
    let name: String

数据管理器:

import Foundation

class DataManager: ObservableObject 
    var allSavedSessions: [ObservationSession] 
        return [ObservationSession(name: "Field mouse droppings"), ObservationSession(name: "Squirrels running up trees"), ObservationSession(name: "Squirrel behavior in urban landscapes")]
    
    
    var allSavedConfigurations: [SessionConfiguration] 
        return [SessionConfiguration(name: "Squirrel Behavior"), SessionConfiguration(name: "Squirrel/Tree Interaction"), SessionConfiguration(name: "Mouse Behavior")]
    

【问题讨论】:

您的代码不可测试,但您不能在body 或其他具有some View 返回类型的计算属性之外调用self.sheet(isPresented: $isObserving)。我的意思是技术上你可以,但是“调用'sheet(isPresented:onDismiss:content:)'的结果是未使用的”。只有在创建视图的地方创建工作表时才会使用该工作表,不是在某些选择处理程序中。 我将完整的演示项目推送到 Github:github.com/dennisbirch/TallyDemo。如果你愿意,你可以抓住它并运行它。如果我正确理解您的评论,听起来您是在说您只能从给定视图呈现单个视图,这意味着我正在尝试的方法(对用户输入做出反应)是不可能的。这似乎是一个巨大的限制。 【参考方案1】:

睡了一夜之后,我想出了一个似乎可行的方法。

我在我的 SessionConfiguration 类型的 DataManager 类中添加了一个“currentConfiguration”属性,并在用户从列表中选择配置时在 ConfigurationsListView 中设置该属性。然后 SessionsListView 可以显示 ConfigurationsListView 或 ObservationsView,具体取决于跟踪流的变量:

import SwiftUI

enum SessionListPresentationFlow 
    case configuration
    case observation


struct SessionsListView: View 
    @ObservedObject var dataManager: DataManager
    @State private var isPresented = false
    @State var flow: SessionListPresentationFlow = .configuration
    var body: some View 
        VStack 
            List 
                ForEach(dataManager.allSavedSessions) session in
                    NavigationLink(
                        // Navigate to a detail view
                        destination: SessionDetailView(session: session),
                        label: 
                            Text("\(session.name)")
                        )
                
            
            Spacer()
            Button("New Session") 
                isPresented = true
            
            .padding()
            .font(.headline)
            .sheet(isPresented: $isPresented, onDismiss: 
                if flow == .observation 
                    flow = .configuration
                 else 
                    flow = .configuration
                
                dataManager.currentConfiguration = nil
                isPresented = false
            ) 
                // Present a view for the appropriate flow
                viewForCurrentFlow()
            
        
    
    
    @ViewBuilder private func viewForCurrentFlow() -> some View 
        if flow == .configuration 
            NavigationView 
                ConfigurationsListView(dataManager: dataManager, selectionHandler:  config in
                    isPresented = false
                    handleConfigSelection(config)
                )
                .navigationTitle("Configurations")
                .navigationBarItems(trailing: Button("Cancel") 
                    isPresented = false
                    flow = .observation
                )
            
         else if flow == .observation, let config = dataManager.currentConfiguration 
            NavigationView 
                ObservationsView(configuration: config)
                    .navigationBarItems(leading: Button(action: isPresented = false) 
                        Text("Done")
                    )
            
         else 
            EmptyView()
        
    
    
    private func handleConfigSelection(_ config: SessionConfiguration) 
        flow = .observation
        isPresented = true
    

【讨论】:

以上是关于如何在基于 SwiftUI 的 iOS 应用中呈现连续视图?的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:如何在单击 ActionSheet 按钮时呈现新视图?

SwiftUI 工作表在第一次呈现时被关闭

SwiftUI - edgesIgnoringSafeArea 在 iOS 13.4 中的行为不同

iOS/iPadOS 中的 SwiftUI + DocumentGroup:如何重命名当前打开的文档

在 iOS16 中用 SwiftUI 图表定制一个线图

如何在 SwiftUI 中呈现全屏 AVPlayerViewController