How can I easily save the Window size and position state using Obj-C?

前端 未结 9 487
灰色年华
灰色年华 2021-02-02 09:19

What is the best way to remember the Windows position between application loads using Obj-C? I am using Interface Builder for the interface, is it possible to do this with bind

相关标签:
9条回答
  • 2021-02-02 09:28

    Got sick and tired of Apples AutoSave and IB BS which sometimes does and sometimes doesn't work and depends on flag settings in System Prefs blah blah blah. Just do this, and it ALWAYS WORKS and even remembers users full screen state!

    -(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];
    }
    
    0 讨论(0)
  • 2021-02-02 09:36

    Put a name that is unique to that window (e.g. "MainWindow" or "PrefsWindow") in the Autosave field under Attributes in Interface Builder. It will then have its location saved in your User Defaults automatically.

    To set the Autosave name programmatically, use -setFrameAutosaveName:. You may want to do this if you have a document-based App or some other situation where it doesn't make sense to set the Autosave name in IB.

    Link to documentation.

    0 讨论(0)
  • 2021-02-02 09:37

    Based on onmyway133's answer I wrote a RestorableWindowController class. As long as your window controller inherits from it, position and size for your windows are restored.

    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)
        }
    
    }
    
    0 讨论(0)
  • 2021-02-02 09:39

    In Swift:

    class MainWindowController : NSWindowController {
        override func windowDidLoad() {
            shouldCascadeWindows = false
            window?.setFrameAutosaveName("MainWindow")
    
            super.windowDidLoad()
        }
    
    0 讨论(0)
  • 2021-02-02 09:40

    In Swift 5.2, in your NSWindowController class:

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

    That's all there is to it!

    0 讨论(0)
  • 2021-02-02 09:42

    I tried all the solutions. It can only saves the position, not the size. So we should do that manually. This is how I do it on my GifCapture app 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)
      }
    }
    
    0 讨论(0)
提交回复
热议问题