【发布时间】:2010-04-29 05:04:11
【问题描述】:
使用 Obj-C 在应用程序加载之间记住 Windows 位置的最佳方法是什么?我正在为接口使用 Interface Builder,是否可以通过绑定来做到这一点。
推荐的方法是什么?谢谢。
【问题讨论】:
标签: objective-c cocoa macos
使用 Obj-C 在应用程序加载之间记住 Windows 位置的最佳方法是什么?我正在为接口使用 Interface Builder,是否可以通过绑定来做到这一点。
推荐的方法是什么?谢谢。
【问题讨论】:
标签: objective-c cocoa macos
在 Interface Builder 的 Attributes 下的 Autosave 字段中输入一个对该窗口唯一的名称(例如“MainWindow”或“PrefsWindow”)。然后它将自动将其位置保存在您的用户默认值中。
要以编程方式设置自动保存名称,请使用-setFrameAutosaveName:。如果您有基于文档的应用程序或其他在 IB 中设置自动保存名称没有意义的情况,您可能需要这样做。
【讨论】:
-windowDidLoad方法中,调用:[super windowDidLoad];[self setShouldCascadeWindows:NO];[self setWindowFrameAutosaveName:@"MyWindowAutosaveName"];
在斯威夫特中:
class MainWindowController : NSWindowController {
override func windowDidLoad() {
shouldCascadeWindows = false
window?.setFrameAutosaveName("MainWindow")
super.windowDidLoad()
}
【讨论】:
根据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.
【讨论】:
我尝试了所有解决方案。它只能保存位置,不能保存大小。所以我们应该手动完成。这就是我在 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)
}
}
【讨论】:
在 Swift 5.2 中,在您的 NSWindowController 类中:
override func windowDidLoad() {
super.windowDidLoad()
self.windowFrameAutosaveName = "SomeWindowName"
}
仅此而已!
【讨论】:
根据 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)
}
}
【讨论】:
为了恢复窗口,您可以在 Interface Builder 中设置恢复 ID。这将用作框架存储在 NSUserDefaults 中的键的一部分。 -- 但这并不(总是)对我有用。
NSWindow 有setFrameUsingName(_:) 等来配置这个,就像@BadmintonCat 写的那样,你也可以手动序列化窗口位置,以防万一也不起作用。
我的应用程序中最简单的解决方案是使用NSWindowController.windowFrameAutosaveName 属性并将其设置为awakeFromNib(_:) 中的某个值。该单行影响加载和保存成功。
【讨论】:
厌倦了 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];
}
【讨论】:
对我来说,应用程序委托中-applicationDidFinishLaunching 中的以下行可以正常工作(在 Catalina,macOS 10.15 下):
[self.window setFrameAutosaveName: @"NameOfMyApp"];
这条线很重要
[self.window setDelegate: self];
在setFrameAutosaveName-applicationDidFinishLaunching中执行之前!
【讨论】:
和其他人一样,我发现以编程方式设置它是可行的......
self.windowFrameAutosaveName = NSWindow.FrameAutosaveName("MyWindow")
但前提是您没有在 IB 中也设置它! 如果你同时设置它......你又回到不工作了。
顺便说一句:我通过在代码末尾添加“WTF”来发现这一点,然后突然一切正常! ?
【讨论】: