如何在不影响/更新其下的内容的情况下构建“浮动”文本视图?

问题描述 投票:0回答:0

我想在我的应用程序显示的_每个_屏幕顶部添加一个小的“浮动”文本视图,显示当前视图编号(viewID):

iPhone Simulator with small red floating text

注意槽口正上方的红色小“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 SE1 with floating red text

在我的第一代 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 被重新绘制(这不应该发生)。

swiftui floating
© www.soinside.com 2019 - 2024. All rights reserved.