我正在使用 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()
}
}
}
}
有办法解决这个问题吗?我了解什么是不透明类型以及编译器告诉我什么,但我不确定如何解决它并实现我想要的功能。
问题是,当你说你的函数的返回值是
some Scene
时,Swift 期望单一类型,正如你提到的。函数/结果构建器通常确实解决了这个问题,例如 ViewBuilder
、SceneBuilder
(您可能需要这里)和其他方法,通过能够获取符合返回协议(此处场景)的多个项目并将它们转换为单个项目.
App
的 var body
,您会发现这是一个 @SceneBuilder
。 SceneBuilder
可以将多个Scene
作为闭包输入,它们将被转换为另一个Scene
。但是,@SceneBuilder
(与 @ViewBuilder
不同)无法处理 if-else 子句,如 SceneBuilder 的 buildOptional
方法中所述。
SceneBuilder 中的条件语句可以包含 if 语句,但不能包含 else 语句,并且条件只能执行编译器检查可用性 [...]。
您可能想要尝试的解决方法是将条件内容放入 ViewBuilder 中的单个始终存在的 MenuBarExtra 中,您可以在其中使用 if-else 子句。我不明白为什么在你的例子中这是不可能的,你的 if-else 的两个分支都创建了一个 MenuBarExtra,只是内容不同。
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()
}
}
请参阅此线程了解如何创建反向绑定。