为 Lion 的用户界面恢复功能编码 NSViewController

Posted

技术标签:

【中文标题】为 Lion 的用户界面恢复功能编码 NSViewController【英文标题】:Encoding NSViewController for Lion's user interface resume feature 【发布时间】:2013-02-13 01:25:05 【问题描述】:

知道将NSViewController 归档在窗口内以用于resume(用户界面保留)目的的最佳做法是什么?我尝试在窗口控制器的 encodeRestorableStateWithCoder: 方法中将其归档,结果发现在调用 restoreStateWithCoder: 时视图控制器没有被取消归档。

// 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;
    

请注意,此视图控制器包含管理自己的子视图的其他视图控制器,因此需要在 NSCoder 实例中进行一些范围界定 - 只需向下传递提供的 coder 对象将导致存档中的名称冲突。

提前致谢!

【问题讨论】:

它可能使用了安全编码,你试过-decodeObjectOfClass:forKey:吗? 【参考方案1】:

状态恢复在NSView 上免费工作,但在NSViewController 上被忽略,即使它将方法实现为NSResponder 的子类。我想这是因为窗口不知道可能拥有它包含的某些视图的 NSViewControllers。

在 OS X Yosemite 上它应该可以工作,因为 NSWindow 现在真正支持 NSViewControllers,但在我的测试用例中没有。我想这是因为人们需要使用新的 API 来“链接” NSViewController 来添加/删除它们,而不是在一边创建它们,然后直接将它们的视图添加到窗口中。如果您想让您的应用在优胜美地之前的系统上运行,则实际上需要后者。

以下是让它始终工作的方法:只需代理 NSViewNSViewController 之间的恢复 API 调用。

子类NSView 像这样:

@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

NSViewController 这样:

@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

现在您可以从 NSViewController 子类调用 -invalidateRestorableState,而 Cocoa 认为它正在与 NSView 对话,会根据需要在您的 NSViewController 子类上自动调用 -encodeRestorableStateWithCoder:-restoreStateWithCoder:

【讨论】:

嗨,这是一个不错的技巧 :) 我也在尝试在 Mac 应用程序上实现恢复,即使是 NSView 子类我也没有收到恢复调用。知道那可能是什么吗?我为此创建了一个新线程:***.com/q/42010026/3251155【参考方案2】:

我对可恢复状态的处理并不多(Jonathon Mah 是为 DL3 做的),但如果我这样做,我会尝试删除这两个方法并实现 +restorableStateKeyPaths,例如:

+ (NSArray *)restorableStateKeyPaths;

    return @[@“contentViewController.firstInterestingStateProperty”, @“contentViewController.secondInterestingStateProperty”];

然后看看机器是否为我处理了这一切。

+ (NSArray *)restorableStateKeyPaths;

返回一组关键路径,表示属性的路径 应该是持久的。框架将通过以下方式观察这些关键路径 KVO 并自动将其值持久化为持久化的一部分 状态,并在重新启动时恢复它们。关键路径的值 应实施密钥归档。基本实现返回一个 空数组。

【讨论】:

如果contentViewController 是窗口控制器的同一个类(因此具有相同的可恢复属性集),这将起作用。不幸的是,视图控制器可以在很多类之间交换。想想官方 Twitter 应用程序,根据您在左侧“选项卡”栏中选择的内容,可交换视图。同样,视图控制器也可以根据它们控制的***视图进行交换。

以上是关于为 Lion 的用户界面恢复功能编码 NSViewController的主要内容,如果未能解决你的问题,请参考以下文章

iOS 5/Lion Core Data 有序关系:指定顺序?

NSView如何实现类似于UIView中的clipsToBounds功能(即不切割其SubView)

如何使用曲线为 NSView 设置动画

防止我的 Cocoa 应用程序“恢复”?

Mac OS Lion 和沙盒

将 NSWindow 显示为 NSView 的子视图