绑定函数类型FocusedSceneValue导致窗口调整菜单命令消失
SwiftUI函数类型FocusedSceneValue导致窗口菜单命令消失的解决方案
问题现象
开发的SwiftUI应用中:
- ContentView包含一个矩形,填充色通过定时器每3秒自动切换,也可通过菜单手动切换
- 矩形上的文本可通过菜单控制显示/隐藏
- 使用
FocusedValues绑定视图状态与菜单,功能正常,但当窗口菜单处于打开状态时,定时器触发视图重渲染会导致「进入全屏」「填充」等系统窗口调整命令消失,需重新打开菜单才会恢复 - 注释掉函数类型的
.focusedSceneValue(\.changeColor, changeColor)后,问题消失
问题原因
直接传递函数类型的changeColor到FocusedValues时,每次视图重渲染都会生成新的函数实例,导致FocusedValues中的对应条目频繁更新。当系统菜单处于打开状态时,这种频繁的状态变更会干扰菜单的内部状态维护,最终导致内置窗口命令丢失。而布尔类型的Binding是引用类型,重渲染时不会生成新实例,因此不会引发问题。
解决方案
将视图状态与操作逻辑封装到ObservableObject中,通过传递对象引用替代直接传递函数,确保FocusedValues中的条目稳定,避免频繁更新干扰菜单状态。
修改后的完整代码
1. 定义ViewModel封装状态与逻辑
import SwiftUI class AppViewModel: ObservableObject { @Published var color: Color = .red @Published var infoTextVisible: Bool = false /// 切换颜色的逻辑封装到ViewModel方法中 func toggleColor() { color = color == .red ? .blue : .red } }
2. 更新ContentView绑定ViewModel
import SwiftUI import Combine struct ContentView: View { @StateObject private var vm = AppViewModel() let timer = Timer.publish(every: 3.0, on: .main, in: .common).autoconnect() var body: some View { ZStack { Rectangle() .fill(vm.color) if vm.infoTextVisible { Text("INFO TEXT") } } // 传递ViewModel引用到FocusedValues .focusedSceneValue(\.appViewModel, vm) .onReceive(timer) { _ in vm.toggleColor() } } }
3. 更新FocusedValues扩展与MenuCommands
import SwiftUI extension FocusedValues { @Entry var appViewModel: AppViewModel? } struct MenuCommands: Commands { @FocusedValue(\.appViewModel) var vm var body: some Commands { CommandMenu("Playback") { Button("Change Color") { vm?.toggleColor() } .keyboardShortcut(.rightArrow, modifiers: []) .disabled(vm == nil) } CommandGroup(before: .sidebar) { Button("Show/Hide Info Text") { vm?.infoTextVisible.toggle() } .keyboardShortcut("I", modifiers: []) .disabled(vm == nil) } } }
4. 保持TestApp.swift不变
import SwiftUI @main struct CarouselApp: App { var body: some Scene { WindowGroup { ContentView() } .commands { MenuCommands() } } }
原理说明
通过ObservableObject封装状态与操作后,传递给FocusedValues的是ViewModel的对象引用,视图重渲染时引用不会改变,FocusedValues中的条目保持稳定,不会触发频繁更新,从而避免干扰系统菜单的状态维护,解决了窗口命令消失的问题。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

