我想在我的应用程序显示的_每个_屏幕顶部添加一个小的“浮动”文本视图,显示当前视图编号(viewID):
注意槽口正上方的红色小“11”。
我有一个设置菜单,可以通过左侧的汉堡包按钮访问,用户可以在其中切换“开发人员模式”以显示或隐藏这个小 viewID。
我的实现有一个控制器单例......
class DebugController: ObservableObject {
public static var shared = DebugController()
@AppStorage("developerMode") var developerMode: Bool = false
@Published var viewID: Int = 0
@MainActor func setViewID(_ newID: Int) -> Void {
viewID = developerMode ? newID : 0
}
init() {
}
}
…由主应用程序分配,然后作为环境对象传递给 ContentView 创建的视图…
@main
struct MyApp: App {
@StateObject private var debugController = DebugController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(debugController)
……但是 ContentView 本身不使用 DebugController。它有一个用于主 NavigationView 的 ZStack,具有相当多的视图层次结构,以及顶部的浮动 ViewIDView。
struct ContentView: View {
var body: some View {
ZStack(alignment: .top) {
NavigationView { // the one and only for all non-sheet views
//- - - a decent view hierarchy here - - -
}
// Show the viewID on top of the app's NavigationView
ViewIDView()
.id("ViewID")
}
}
}
只有浮动的ViewIDView监听DebugController发布的viewID值。如果它是 0 则字符串被清除(“”),否则 Int 值被转换为 String 并显示在 Text 项中。
struct ViewIDView: View {
@EnvironmentObject private var debugController: DebugController
var body: some View {
let _ = Self._printChanges()
let idString = debugController.viewID > 0 ? String(debugController.viewID)
: ""
HStack {
Spacer()
Spacer()
Text(idString)
.font(.caption2)
.foregroundColor(.red)
.edgesIgnoringSafeArea(.top)
Spacer()
}
}
}
Spacer 需要将文本稍微向右移动一点,因为带有 TouchID 的 iPhone 没有缺口,但会在中间显示时间。
在我的第一代 iPhone SE 上,它非常适合左右两个垫片。
这在导航堆栈的前两层工作正常。每当将新视图压入堆栈时,它都会设置单例 DebugController
的全局 viewID 值 .onAppear() {
DebugController.shared.setViewID(VIEW_11)
}
然后从 DebugController 发布,并导致 ViewIDView 中的红色文本更新。
但是,当我使用 NavigationLink 在导航堆栈上推送第三个视图时,它在出现时会更新 viewID 编号,然后 SwiftUI 也会更新主 ContentView 主体,这不应该发生,从而导致视图中的新实例层次结构,然后再次调用它们的 .onAppear,用错误的值覆盖全局 viewID。更糟糕的是,它的 .task 也被再次调用,它加载了更多数据并破坏了我首先想要显示的数据。
当我只是在这个第三级视图的 .onAppear() 中注释掉调用 DebugController.shared.setViewID(VIEW_123) 时,SwiftUI 不会更新主 ContentView 并且我的第三级视图显示正确的数据 - 当然除了浮动 viewID没有得到更新,仍然显示二级视图编号。
所以,我这里的问题是,更新浮动的 Debug ViewIDView 也会更新它下面的 Navigation 堆栈,这一定不会发生。
我的方法有什么问题?
我怎样才能实现一个真正的浮动视图,它可以从任何地方更新而不影响应用程序的其余部分?
编辑:
@ScottM 建议使用 .overlay 而不是 ZStack:
struct ContentView: View {
var body: some View {
NavigationView { // the one and only for all non-sheet views
//- - - a decent view hierarchy here - - -
}.overlay(debugOverlay, alignment: .top)
}
@ViewBuilder private var debugOverlay: some View {
// Show the viewID on top of the app's NavigationView
ViewIDView()
.id("ViewID")
}
}
但不幸的是,这根本没有改变任何东西。 在我更新 viewID 值后,我仍然看到 NavigationView 被重新绘制(这不应该发生)。