有条件地渲染 MenuBarExtra

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

我正在使用 SwiftUI 编写 macOS 应用程序,并且我的应用程序包含一个菜单栏项。我想根据某些全局设置显示两种“类型”的菜单栏项目之一(一种带有滴答计时器,一种是静态图像)。

但是,我遇到了一个问题,Swift 期望底层

Scene
类型相同,但事实并非如此,因为我使用了两种不同的初始值设定项:

@main
struct MyApp: App {
    var body: some Scene {
        self.menuBarExtra()
        ContentView()
    }

    // ERROR: Function declares an opaque return type 'some Scene', but the return statements in its body do not have matching underlying types
    private func menuBarExtra() -> some Scene {
        if $showTimerInMenuBar {
            return MenuBarExtra(content: {AppMenu()}, label: {Text(timeRemainingFormatted)})
        }
        else {
            return MenuBarExtra("App", systemImage: "someImage") {
                AppMenu()
            }
        }
    }
}

有办法解决这个问题吗?我了解什么是不透明类型以及编译器告诉我什么,但我不确定如何解决它并实现我想要的功能。

swift macos swiftui
1个回答
0
投票

问题是,当你说你的函数的返回值是

some Scene
时,Swift 期望单一类型,正如你提到的。函数/结果构建器通常确实解决了这个问题,例如
ViewBuilder
SceneBuilder
(您可能需要这里)和其他方法,通过能够获取符合返回协议(此处场景)的多个项目并将它们转换为单个项目.

但是,遗憾的是,目前您想要的场景是不可能的。如果您查看

App
var body
,您会发现这是一个
@SceneBuilder
SceneBuilder
可以将多个
Scene
作为闭包输入,它们将被转换为另一个
Scene
。但是,
@SceneBuilder
(与
@ViewBuilder
不同)无法处理 if-else 子句,如 SceneBuilder 的
buildOptional
方法中所述。

SceneBuilder 中的条件语句可以包含 if 语句,但不能包含 else 语句,并且条件只能执行编译器检查可用性 [...]。

  1. 您可能想要尝试的解决方法是将条件内容放入 ViewBuilder 中的单个始终存在的 MenuBarExtra 中,您可以在其中使用 if-else 子句。我不明白为什么在你的例子中这是不可能的,你的 if-else 的两个分支都创建了一个 MenuBarExtra,只是内容不同。

  2. MenuBarExtra 似乎还具有 带有

    isInserted
    参数的初始化器,这可能正是您所需要的:

     @SceneBuilder
     private func menuBarExtra() -> some Scene {
         MenuBarExtra(isInserted: $showTimerInMenuBar, content: { AppMenu() }, label: { Text(timeRemainingFormatted) })
    
         MenuBarExtra("App", systemImage: "someImage", isInserted: [an inverted Binding to showTimerInMenuBar]) {
             AppMenu()
         }
     }
    

    请参阅此线程了解如何创建反向绑定。

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