UI Save/Restoration Cocoa 中的机制通过 Swift

UI Save/Restoration mechanism in Cocoa via Swift

我想保存 Check Box 的状态,退出应用程序,然后再次启动 macOS 应用程序以查看 Check Box 的恢复状态。但是我的应用程序 UI 中没有恢复状态。

我做错了什么?

import Cocoa

class ViewController: NSViewController {

    @IBOutlet weak var tick: NSButton!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func encodeRestorableState(with coder: NSCoder) {
        super.encodeRestorableState(with: coder)
        coder.encode(tick.state, forKey: "")
    }

    override func restoreState(with coder: NSCoder) {
        super.restoreState(with: coder)       
        if let state = coder.decodeObject(forKey: "") as? NSControl.StateValue {
            tick.state = state
        }
    }
}

据我所知,这是实现自定义 UI 状态恢复 window and/or 其内容所需的绝对最低限度。

在这个例子中,我有一个带复选框的 window,该复选框的状态代表我想在应用程序重新启动时恢复的一些自定义视图状态。

该项目包含一个 window 和一个复选框按钮。按钮的值绑定到 window 的内容视图控制器的 myState 属性。因此,从技术上讲,这是一个复选框控件这一事实是无关紧要的;我们实际上要保留和恢复 myState 属性(UI 会自行处理)。

为了完成这项工作,window 的 restorable 属性 设置为 true(在 window 对象检查器中)并且 window 被分配了一个标识符 ("PersistentWindow")。 NSWindow 是 subclassed (PersistentWindow) 并且 subclass 实现了 restorableStateKeyPaths 属性。此 属性 列出了要 preserved/restored.

的自定义属性

注意:如果您可以根据键值兼容 属性 路径列表来定义 UI 状态恢复,那是(到目前为止)最简单的解决方案。如果没有,则必须实现 encodeRestorableState / restoreState 并负责调用 invalidateRestorableState.

这是习俗 window class:

class PersistentWindow: NSWindow {

    // Custom subclass of window the perserves/restores UI state

    // The simple way to preserve and restore state information is to just declare the key-value paths
    //  of the properties you want preserved/restored; Cocoa does the rest
    override class var restorableStateKeyPaths: [String] {
       return [ "self.contentViewController.myState" ]
       }

    // Alternatively, if you have complex UI state, you can implement these methods
//  override func encodeRestorableState(with coder: NSCoder) {
//      // optional method to encode special/complex view state here
//  }
//  
//  override func restoreState(with coder: NSCoder) {
//      // companion method to decode special/complex view state
//  }

}

这是内容视图控制器的(相关部分)

class ViewController: NSViewController {

    @objc var myState : Bool = false

    blah, blah, blah
}

(我将其构建为 Cocoa 应用程序项目,如果有人告诉我可以将其上传到何处,我可以上传它。)

实际上,如果您不想,则不必通过 restorableStateKeyPaths / KVO / KVC。

我被困在和你一样的状态,encodeRestorableState()restoreState() 方法没有被调用,但发现缺少了什么。

  1. 在“系统偏好设置”>“常规”中,确保取消选中 "Close windows when quitting an app"。
  2. 确保包含您的视图的 NSWindow 在 IB 中启用了 "Restorable" 行为。
  3. 确保您的 NSViewController 设置了 "Restoration ID"。
  4. 除非您调用 invalidateRestorableState(),否则您的 NSViewController 不会被编码。每次 NSViewController 中的状态发生变化并且您想保存时,您都需要调用它。
  5. NSViewController 恢复后状态没有变化时,关闭应用程序时不会再次编码其状态。这会导致自定义状态在重新启动应用程序时无法恢复。我发现的最简单的方法是在 viewDidLoad() 中也调用 invalidateRestorableState(),这样状态就会一直保存。

完成所有这些之后,我什至不必另外实现 NSApplicationDelegateNSWindowRestoration 协议方法。所以 NSViewController 的状态恢复是非常独立的。只有外部 属性 是可恢复的 NSWindow.

在为这个问题浪费了几个小时之后,我终于让它工作了。其他答案中的一些信息有帮助,一些缺失,一些不是必需的。

这是我基于新 Xcode 13 项目的最小示例:

  1. 在 AppDelegate 中添加 (其他示例中缺少)
    func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { return true }
    
  2. 在ViewController中添加:
    @objc var myState : Bool = false
    
    override class var restorableStateKeyPaths: [String] {
       return [ "myState" ]
    }
    
  3. 设置一些 UI 并将其绑定到 myState 以查看发生了什么
  4. 确保取消选中“系统偏好设置”>“常规”>“退出应用程序时关闭 windows”

不需要做的事情:

  • 创建自定义 window 子类
  • 设置自定义恢复 ID
  • 仅与 Xcode start/stop
  • 配合使用效果很好