如何在基于 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 - edgesIgnoringSafeArea 在 iOS 13.4 中的行为不同