Re: How to implement NSWindowRestoration protocol in Swift
Re: How to implement NSWindowRestoration protocol in Swift
- Subject: Re: How to implement NSWindowRestoration protocol in Swift
- From: Quincey Morris <email@hidden>
- Date: Thu, 03 Mar 2016 10:32:22 -0800
- Feedback-id: 167118m:167118agrif8a:167118sC69BMK-pX:SMTPCORP
On Mar 3, 2016, at 05:27 , Bill Cheeseman <email@hidden> wrote:
>
> I use MainWindowController as the restoration class:
>
> static func restoreWindowWithIdentifier(identifier: String, state: NSCoder, completionHandler: (NSWindow?, NSError?) -> Void) {
> let controller = MainWindowController()
> var restoreWindow: NSWindow? = nil
> if identifier == "MainWindow" {
> restoreWindow = controller.window
> }
> completionHandler(restoreWindow, nil);
> }
>
> However, how do I fit this into the storyboard context? When I create an instance of MainWindowController here, I seem to be bypassing the segues in the storyboard. It occurs to me that maybe I should make AppDelegate the restoration class, instead of MainWindowController. The documentation is cryptic, even in the NSWindowRestoration.h header file comments. I'm thinking that then I could just restore the previously encoded underlying data model values and let the storyboard run by itself as usual. But it isn't clear to me what will become of the temporary window controller. Or, in other words, how do I find or create a valid window in this method, as the header file comments say I should?
— There’s nothing wrong with using MainWindowController as your restoration class (AFAIK), because the restoration method is a static func.
OTOH, there’s no particular reason, in *this* part of the restoration mechanism, to tie restoration to the window controller at all. In a sense, the window controller has nothing to do with it, except to the extent that the timing of its instantiation determines the time of window loading.
There may be other reasons to tie restoration to the window controller. I’ll come back to that point later.
— Ignoring the rest of your app for a moment, if you’re using a storyboard then creating a naked MainWindowController here is the wrong thing to do. Instead, instantiate a MainWindowController from the storyboard in the “usual” way:
> let storyboard = NSStoryboard (name: "MainStoryboard", bundle: nil)
> let controller = storyboard.instantiateControllerWithIdentifier (“Main Window Controller") as! MainWindowController
This isn’t a *temporary* window controller, it’s the real thing. That means the next step is to store the window controller reference wherever your app normally keeps that reference: perhaps as a property of the app delegate. Lastly, you invoke the completion handler to complete the window restoration process (the controller.window reference causing the window to be loaded as usual).
— But you need to be aware of what the rest of your app is doing at startup. If your ‘main’ function instantiates the window controller unconditionally, then you don’t want to instantiate a second one. You have a couple of choices of what to do. You can go ahead and create it as you do now, then have your state restoration static func *find* the window and initiate restoration on that. (That’s what the header file comment "It is acceptable to invoke the completion handler with a pre-existing window…” means: legacy code might have created the window already, and you might wish not to change that.) Your restoration method wouldn’t actually create anything in that case.
In these circumstances, it might indeed make sense to use the app delegate as the restoration class, especially if it’s storing a pointer to the window controller privately anyway.
Or you can remove that initial instantiation, leave the controller instantiation in the state restoration static func as above, check (in application didFinishLaunching) to see if a window controller exists due to state restoration, and create it if not. This sounds like an excess of trouble, but it’s what you’d do if you want to get to state restoration *before* the window was actually loaded, for example if you wanted to create one of a number of different NSWindow subclasses depending on state.
— Back to window controllers. If you have no custom state restoration, I think I’d put the state restoration code in the app delegate and be done with it. Looking back at the code in an old project, I see that’s what I did with the preferences window, even though it’s document-based application. In the app delegate (Obj-C because it’s old):
> NSWindow* window = nil;
>
> if ([identifier isEqualToString: @"GRAPreferencesWin"])
> window = [GRAPrefWinController sharedWindowController].window; // creates the window controller if it does not exist
>
> completionHandler (window, nil);
However, if you do have custom state restoration, then I think it’s easier to put methods like encodeRestorableStateWithCoder and restoreStateWithCoder in the window controller, since there is going to be a custom class anyway. Otherwise, you’d have to subclass NSWindow, and put what I think of as controller logic in the window instead of the controller.
In *this* configuration, it might make sense to put restoreWindowWithIdentifier in the window controller class after all, to keep all the related code together.
I agree with you that this is poorly documented and under-documented. In a document-based app, some important parts of the process happen automatically in NSDocumentController. In most cases, though, it’s very little code, if only you can work out where to put it.
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden