在我的应用程序中,我有一个 tabView,其中包含两个呈现相同视图的选项卡。每个视图中的内容在各自的视图中规定。这些视图有一个 .sheet 修饰符,当前基于
@Published var showSheet
类中定义的 ObservableObject
触发。
我看到的问题发生在以下情况:
点击按钮后,工作表会弹出,但随后会立即消失,无需用户交互。
目标是让工作表正常打开和关闭,同时也能够正常切换选项卡。
在我看来,
triggerSheet
是在到达新标签时出现的,但我不明白为什么。我能够使用类似于下面的代码的内容重现该问题,希望我想要完成的任务的想法很清楚。
代码:
标签视图:
struct TabView: View {
@StateObject private var obsPresentation = ObsPresentation.shared
var body: some View {
ZStack {
VStack {
TabView(selection: $obsPresentation.currentTab) {
if obsPresentation.conditionOne {
HealthView()
.tabItem {
Label("Person 1 Health", systemImage: "heart")
}
.tag(obsPresentation.Tab.health1)
}
if obsPresentation.conditionTwo {
HealthView()
.tabItem {
Label("Person 2 Health", systemImage: "heart")
}
.tag(obsPresentation.Tab.health2)
}
}
}
}
}
}
健康视图:
struct HealthView: View {
@StateObject private var obsPresentation = ObsPresentation.shared
var body: some View {
VStack() {
ScrollView {
LazyVGrid() {
ForEach(...) { _ in
Button(action: {
obsPresentation.showSheet()
}) {
... UI
}
}
}
}
}
.sheet(isPresented: $obsPresentation.triggerSheet) {
SheetView()
}
}
}
观察对象类:
public class ObsPresentation: ObservableObject {
public static var shared = ObsPresentation()
public enum Tab {
case health1, health2
}
@Published public var currentTab: Tab = .health1 {
didSet {
switch tabSelection {
case .health1:
...
case .health2:
...
default: break
}
}
}
// These conditions are manipulated elsewhere
@Published public var condition1 = true
@Published public var condition2 = true
@Published var triggerSheet = false
func showSheet() {
triggerSheet = true
}
}
这里显而易见的答案是将绑定sheetTrigger bool 本地化为HealthView() 作为适用于工作表演示的
@State var
。但是,我的应用程序还支持深度链接,某些特定链接需要导航到选项卡并立即打开工作表。因此,sheetTrigger bool 必须可以在视图外部访问,以便可以通过深度链接逻辑进行切换。
我还尝试创建一个单独的
ObservableObject
类,其方式与 ObsPresentation
类似,仅处理基于工作表的代码。这导致了同样的行为。
谢谢你
主要问题是您使用的是
shared
状态对象,该对象设置为 true
,并且根据您提供的代码从未设置为 false
。这意味着当您第一次呈现该工作表时,它会以您期望的方式运行,但是当您第二次呈现该工作表时,它只会短暂呈现然后被忽略,因为您已在第二次有效地将布尔值切换为 false
按钮。看起来像这样,按执行顺序;初始化 false
-> 点击按钮 -> 设置 true
-> 工作表呈现 -> 工作表消失 -> 点击第二个按钮 -> 设置 false
-> 奇怪的行为。
我还注意到您使用
$obsPresentation.triggerSheet
而不是 obsPresentation.$triggerSheet
这也可能导致问题,但我在下面推荐一个更好的解决方案。我现在没有 IDE,所以我无法确认这种直觉。基本上你是在告诉它,“嘿,当 obsPresentation 更新时更新视图!”而不是“嘿,当triggerSheet ON obsPresentation 的属性更新时更新视图!”
所以,如果您已经读到这里,那么您肯定想知道,既然该工作表设置为
false
,为什么它甚至会出现。这是因为工作表具有独特的行为,它们在动画中期时会忽略某些线程操作。您的实现正在触发工作表出现,因为 @State
正在更新,并且在 ViewBuilder
的上下文中,它不关心它是 true
还是 false
,只是它已更新,并且应该重新绘制。然而,当它处于重新绘制的中间时,它的发布值翻转为 false,从而导致工作表消失。通过从父级中解雇工作表的父级,同时提供一个工作表作为证明,也可能会发生类似的奇怪行为。那么如何解决呢?
修复工作表(尤其是用于不同用途的工作表)的最佳方法是使用
Enum
代替布尔值。例如:
enum PresentedSheet {
case sheetOne, sheetTwo, sheetThree
}
如果您要使用可选值,那么只要该值不为零,您就可以显示工作表。例如:
@State presentedSheet: PresentedSheet? = nil
var body: some View {
Text("Example")
.sheet(item: $presentedSheet) {
switch presentedSheet { //Show the view you want }
}
}
在您的情况下,您可以在可观察对象上添加
presentedSheet
代替布尔值。如果您需要其他控件,如果您需要在该工作表关闭时了解或更新某些内容,可以调用回调函数onDismiss
。
最后一点,在使用工作表时,如果您在不同视图之间共享工作表,请确保从最父视图中显示该工作表,或者如果特定的子视图被销毁,它也会自动消除您当前的工作表。提交的表。另外,不要过度使用它,因为它会很快使您的代码变得混乱或使事情变得复杂。