【问题标题】:How can I easily save the Window size and position state using Obj-C?如何使用 Obj-C 轻松保存窗口大小和位置状态?
【发布时间】:2010-04-29 05:04:11
【问题描述】:

使用 Obj-C 在应用程序加载之间记住 Windows 位置的最佳方法是什么?我正在为接口使用 Interface Builder,是否可以通过绑定来做到这一点。

推荐的方法是什么?谢谢。

【问题讨论】:

    标签: objective-c cocoa macos


    【解决方案1】:

    在 Interface Builder 的 Attributes 下的 Autosave 字段中输入一个对该窗口唯一的名称(例如“MainWindow”或“PrefsWindow”)。然后它将自动将其位置保存在您的用户默认值中。

    要以编程方式设置自动保存名称,请使用-setFrameAutosaveName:。如果您有基于文档的应用程序或其他在 IB 中设置自动保存名称没有意义的情况,您可能需要这样做。

    Link to documentation.

    【讨论】:

    • 如果您使用的是 NSWindowController,请不要使用 Interface Builder autoSaveName 字段。请参阅下面的 Gon 的回答。具体在窗口控制器的-windowDidLoad方法中,调用:[super windowDidLoad];[self setShouldCascadeWindows:NO];[self setWindowFrameAutosaveName:@"MyWindowAutosaveName"];
    【解决方案2】:

    在斯威夫特中:

    class MainWindowController : NSWindowController {
        override func windowDidLoad() {
            shouldCascadeWindows = false
            window?.setFrameAutosaveName("MainWindow")
    
            super.windowDidLoad()
        }
    

    【讨论】:

      【解决方案3】:

      根据doc,保存一个窗口的位置:

      NSWindow *window = // the window in question
      [[window windowController] setShouldCascadeWindows:NO];      // Tell the controller to not cascade its windows.
      [window setFrameAutosaveName:[window representedFilename]];  // Specify the autosave name for the window.
      

      【讨论】:

        【解决方案4】:

        我尝试了所有解决方案。它只能保存位置,不能保存大小。所以我们应该手动完成。这就是我在 GifCapture 应用程序上的操作方式https://github.com/onmyway133/GifCapture

        class MainWindowController: NSWindowController, NSWindowDelegate {
        
          let key = "GifCaptureFrameKey"
        
          override func windowDidLoad() {
            super.windowDidLoad()
        
            NotificationCenter.default.addObserver(self, selector: #selector(windowWillClose(_:)), name: Notification.Name.NSWindowWillClose, object: nil)
          }
        
          override func awakeFromNib() {
            super.awakeFromNib()
        
            guard let data = UserDefaults.standard.data(forKey: key),
              let frame = NSKeyedUnarchiver.unarchiveObject(with: data) as? NSRect else {
                return
            }
        
            window?.setFrame(frame, display: true)
          }
        
          func windowWillClose(_ notification: Notification) {
            guard let frame = window?.frame else {
              return
            }
        
            let data = NSKeyedArchiver.archivedData(withRootObject: frame)
            UserDefaults.standard.set(data, forKey: key)
          }
        }
        

        【讨论】:

          【解决方案5】:

          在 Swift 5.2 中,在您的 NSWindowController 类中:

          override func windowDidLoad() {
              super.windowDidLoad()
              self.windowFrameAutosaveName = "SomeWindowName"
          }
          

          仅此而已!

          【讨论】:

            【解决方案6】:

            根据 onmyway133 的回答,我写了一个RestorableWindowController 课程。只要您的窗口控制器继承自它,您的窗口的位置和大小就会恢复。

            import Cocoa
            
            open class RestorableWindowController: NSWindowController {
            
                // MARK: - Public -
            
                open override func windowDidLoad() {
                    super.windowDidLoad()
            
                    NotificationCenter.default.addObserver(self, selector: #selector(windowWillClose), name: NSWindow.willCloseNotification, object: nil)
                    if let frame = storedFrame {
                        window?.setFrame(frame, display: true)
                    }
                }
            
                open override func awakeFromNib() {
                    super.awakeFromNib()
            
                    if let frame = storedFrame {
                        window?.setFrame(frame, display: true)
                    }
                }
            
                open override var contentViewController: NSViewController? {
                    didSet {
                        if let frame = storedFrame {
                            window?.setFrame(frame, display: true)
                        }
                    }
                }
            
                // MARK: - Private -
            
                private var storedFrameKey: String {
                    String(describing: type(of: self)) + "/storedFrameKey"
                }
                private var storedFrame: NSRect? {
                    guard let string = UserDefaults.standard.string(forKey: storedFrameKey) else {
                        return nil
                    }
                    return NSRectFromString(string)
                }
            
                @objc private func windowWillClose() {
                    guard let frame = window?.frame else {
                        return
                    }
                    UserDefaults.standard.set(NSStringFromRect(frame), forKey: storedFrameKey)
                }
            
            }
            

            【讨论】:

              【解决方案7】:

              为了恢复窗口,您可以在 Interface Builder 中设置恢复 ID。这将用作框架存储在 NSUserDefaults 中的键的一部分。 -- 但这并不(总是)对我有用。

              NSWindowsetFrameUsingName(_:) 等来配置这个,就像@BadmintonCat 写的那样,你也可以手动序列化窗口位置,以防万一也不起作用。

              我的应用程序中最简单的解决方案是使用NSWindowController.windowFrameAutosaveName 属性并将其设置为awakeFromNib(_:) 中的某个值。该单行影响加载和保存成功。

              【讨论】:

                【解决方案8】:

                厌倦了 Apples AutoSave 和 IB BS,它们有时有效,有时无效,并且取决于 System Prefs blah blah blah 中的标志设置。只需执行此操作,它始终可以正常工作,甚至可以记住用户的全屏状态!

                -(void)applicationDidFinishLaunching:(NSNotification *)notification
                {
                    [_window makeKeyAndOrderFront:self];
                
                    // Because Saving App Position and Size is FUBAR
                    NSString *savedAppFrame = [userSettings stringForKey:AppScreenSizeAndPosition];
                    NSRect frame;
                    if(savedAppFrame) {
                        frame = NSRectFromString(savedAppFrame);
                        [_window setFrame:frame display:YES];
                    }
                    else
                        [_window center];
                
                    // Because saving of app size and position on screen doesn't remember full screen
                    if([userSettings boolForKey:AppIsFullScreen])
                        [_window toggleFullScreen:self];
                }
                -(void)windowDidEnterFullScreen:(NSNotification *)notification
                {
                    [userSettings setBool:YES forKey:AppIsFullScreen];
                }
                -(BOOL)windowShouldClose:(NSWindow *)sender
                {
                    // Have to use this to set zoom state because exit full screen state always called on close
                    if(sender == _window) {
                        [userSettings setBool:(_window.isZoomed ? YES:NO) forKey:AppIsFullScreen];
                    }
                    return YES;
                }
                -(void)applicationWillTerminate:(NSNotification *)aNotification
                {
                    [userSettings setObject:NSStringFromRect(_window.frame) forKey:AppScreenSizeAndPosition];
                    [userSettings synchronize];
                }
                

                【讨论】:

                  【解决方案9】:

                  对我来说,应用程序委托中-applicationDidFinishLaunching 中的以下行可以正常工作(在 Catalina,macOS 10.15 下):

                    [self.window setFrameAutosaveName: @"NameOfMyApp"];
                  

                  这条线很重要

                    [self.window setDelegate: self];
                  

                  setFrameAutosaveName-applicationDidFinishLaunching中执行之前

                  【讨论】:

                    【解决方案10】:

                    和其他人一样,我发现以编程方式设置它是可行的......

                    self.windowFrameAutosaveName = NSWindow.FrameAutosaveName("MyWindow")
                    

                    但前提是您没有在 IB 中也设置它! 如果你同时设置它......你又回到不工作了。

                    顺便说一句:我通过在代码末尾添加“WTF”来发现这一点,然后突然一切正常! ?

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2015-10-04
                      • 1970-01-01
                      • 2011-02-20
                      • 1970-01-01
                      相关资源
                      最近更新 更多