【问题标题】:UI Save/Restoration mechanism in Cocoa via Swift通过 Swift 在 Cocoa 中的 UI 保存/恢复机制
【发布时间】:2018-05-14 13:06:30
【问题描述】:

我想保存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 恢复还有很多。 UI 恢复的关键是 NSWindow;视图控制器不会自动包含在内。因此,您必须首先确保您的窗口正在恢复,然后该窗口必须负责保存和恢复它需要的任何属性/子视图/控制器。见UI Preservation

标签: macos cocoa swift4 restore savestate


【解决方案1】:

据我所知,这是实现窗口和/或其内容的自定义 UI 状态恢复所需的绝对最低要求。

在此示例中,我有一个带有复选框的窗口,该复选框的状态表示我希望在重新启动应用程序时恢复的一些自定义视图状态。

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

为了完成这项工作,窗口的restorable 属性设置为true(在窗口对象检查器中),并为窗口分配了一个标识符("PersistentWindow")。 NSWindow 是子类 (PersistentWindow),子类实现 restorableStateKeyPaths 属性。此属性列出要保留/恢复的自定义属性。

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

这是自定义窗口类:

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 应用程序项目,如果有人告诉我可以将其上传到哪里,我可以将其上传。)

【讨论】:

  • 这里是下载链接Persistent UI Swift
  • James,问题是,应用程序只能通过 Xcode 中的构建/停止按钮恢复其状态(例如文本字段)。在 Finder 中通过 Launch/Quit 没有 restore state
  • 确保您没有在系统偏好设置 > 常规设置中选中退出应用程序时关闭窗口选项。选中该框会关闭所有应用的全局自动状态恢复。
  • 还有一个观察结果:使用 Stop 按钮停止您的应用程序相当于强制退出。在这些条件下,状态恢复可能会或可能不会起作用。测试它的正确方法是使用 Quit 命令正常退出应用程序,这允许NSApplication 对象执行其所有正常的关闭内务处理,包括将恢复信息写入磁盘。
  • 是的,你是对的!它现在完美运行。非常感谢!
【解决方案2】:

如果你不想,其实不必经过restorableStateKeyPaths/KVO/KVC。

我被困在与您相同的状态,没有调用 encodeRestorableState()restoreState() 方法,但发现缺少了什么。

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

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

【讨论】:

    【解决方案3】:

    在这个问题上浪费了我几个小时的生命后,我终于让它工作了。其他答案中的一些信息很有帮助,有些缺失,有些没有必要。

    这是我基于新 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. 确保未选中“系统偏好设置”>“常规”>“退出应用程序时关闭窗口”

    不需要做的事情:

    • 创建自定义窗口子类
    • 设置自定义恢复 ID
    • 只需使用 Xcode 启动/停止即可正常工作

    【讨论】:

      猜你喜欢
      • 2017-11-15
      • 2020-11-04
      • 2015-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-04
      • 2018-07-24
      • 1970-01-01
      相关资源
      最近更新 更多