Encoding NSViewController for Lion's user interface resume feature

二次信任 提交于 2019-12-19 01:41:21

问题


Any idea what are the best practices for archiving an NSViewController inside a window for resume (user interface preservation) purposes? I've tried archiving it in the window controller's encodeRestorableStateWithCoder: methods only to find out that the view controller doesn't get unarchived when restoreStateWithCoder: is called.

// NSWindowController subclass

-(void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
    [super encodeRestorableStateWithCoder:coder];
    NSViewController* contentViewController = self.contentViewController;
    if (contentViewController) {
        [coder encodeObject:contentViewController forKey:BSContentViewControllerResumeKey];
    }
}

-(void)restoreStateWithCoder:(NSCoder *)coder
{
    [super restoreStateWithCoder:coder];
    NSViewController* contentViewController = [coder decodeObjectForKey:BSContentViewControllerResumeKey];
    if (contentViewController) {
        // somehow this never get executed since contentViewController always comes out nil
        self.contentViewController = contentViewController;
    }
}

Note that this view controller contains other view controllers that manages their own subviews, and thus will need some scoping in the NSCoder instance – simply passing the provided coder object downwards will cause name clashes in the archive.

Thanks in advance!


回答1:


State restoration works for free on NSView but is ignored on NSViewController even though it implements the methods as a subclass of NSResponder. I suppose that's because the window doesn't know about NSViewControllers that may own some of the views it contains.

On OS X Yosemite it's supposed to work since NSWindow now has real support for NSViewControllers, but it doesn't in my test cases. I guess it's because one needs to "chain" the NSViewControllers using the new APIs to add / remove them vs creating them on the side and just adding their views directly to the window. The latter is actually required if you want to have your app run on pre-Yosemite systems anyway.

Here's how to make it always work: simply proxy the restoration APIs calls between NSView and NSViewController.

Subclass NSView like this:

@interface GIView : NSView
@property(nonatomic, weak) GIViewController* viewController;  // Avoid retain-loops!
@end

@implementation GIView

- (void)setViewController:(GIViewController*)viewController {
  _viewController = viewController;
}

- (void)encodeRestorableStateWithCoder:(NSCoder*)coder {
  [super encodeRestorableStateWithCoder:coder];

  [_viewController encodeRestorableStateWithCoder:coder];
}

- (void)restoreStateWithCoder:(NSCoder*)coder {
  [super restoreStateWithCoder:coder];

  [_viewController restoreStateWithCoder:coder];
}

@end

And NSViewController like this:

@interface GIViewController : NSViewController
@end

@implementation GIViewController

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
  if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
    self.view.viewController = self;  // This loads the view immediately as a side-effect
  }
  return self;
}

- (void)dealloc {
  self.view.viewController = nil;  // In case someone is still retaining the view
}

- (void)invalidateRestorableState {
  [self.view invalidateRestorableState];
}

@end

Now you can call -invalidateRestorableState from the NSViewController subclass and Cocoa, thinking it's talking to an NSView, will automatically call -encodeRestorableStateWithCoder: and -restoreStateWithCoder: on your NSViewController subclass as needed.




回答2:


I haven’t messed with restorable state much (Jonathon Mah did it for DL3) but if I were doing this I’d try deleting those two methods and implement +restorableStateKeyPaths, e.g.:

+ (NSArray *)restorableStateKeyPaths;
{
    return @[@“contentViewController.firstInterestingStateProperty”, @“contentViewController.secondInterestingStateProperty”];
}

And just see if the machinery handled it all for me.

+ (NSArray *)restorableStateKeyPaths;

Returns a set of key paths, representing paths of properties that should be persistent. The frameworks will observe these key paths via KVO and automatically persist their values as part of the persistent state, and restore them on relaunch. The values of the key paths should implement keyed archiving. The base implementation returns an empty array.



来源:https://stackoverflow.com/questions/14844966/encoding-nsviewcontroller-for-lions-user-interface-resume-feature

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!