我有两个.xib文件,MainMenu和PreferenceMenu。我将文件的PreferenceMenu所有者设置为NSWindowController
的子类,以便我可以修改窗口的打开,修改等方式。
通常我通过IB打开PreferenceMenu,将showWindow:
动作发送到Preferences Controller对象。这一直很好,窗户一直打开完美。我还在PreferenceMenu中实施了NSTabView
和NSToolBar
,因为不同的标签包含不同的偏好。
无论如何,我现在必须提示用户是否要在首次加载应用程序后打开PreferenceMenu。我的过程检查是否设置了NSUserDefaults
键,然后提示并设置密钥(如果不是)。如果返回的NSAlert
按钮是特定按钮,则我打开PreferenceMenu并切换到正确的选项卡。
这个过程的要点是:
CCPreferencesController.h
CCAppDelegate.h
strong
窗口(适用于ARC)的CCPreferencesController
引用使用NSAlert
提示,如果返回按钮正确,请运行以下命令:
self.windowController = [[CCPreferencesController alloc] initWithWindowNibName:@"PreferenceMenu"];
[self.windowController showWindow:self];
在CCAppDelegate.h
CCPreferencesController.m
处理NSAlert
中的CCPreferencesController.m
回复:
- (void)showWindow:(id)sender{
if ([CCAppDelegate class] == [sender class]){
[self openLogin];
}
[super showWindow:sender];
}
TabView
方法中切换openLogin
。这更好的工作正常,除了showWindow
之外,在窗口的内容实际加载之前调用。这意味着调用self.toolbar
,self.tabView
等会返回NULL
。我的解决方案是简单地使用一个计时器来等待元素加载,但这远远不够优雅。
我的问题,是如何避免使用延迟,而是在openLogin
上调用我的showWindow
方法,但等待窗口&# 39;要加载的内容?
我还99%确定我的代码非常糟糕,包括我如何导入.h文件和继承文件的所有者,所以非常感谢任何使这更好的提示。< / p>
对于那些想要更详细地查看代码的人,这里是相关部分
CCAppDelegate.h:
#import <Cocoa/Cocoa.h>
#import "CCPreferencesController.h"
@class WebView;
@interface CCAppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate, NSSharingServiceDelegate>
@property (strong) CCPreferencesController *windowController;
...
@end
CCAppDelegate.m:
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame{
bool loginPrompted = [[NSUserDefaults standardUserDefaults] boolForKey:@"loginPrompted"];
if (!loginPrompted){
NSAlert *alert = [NSAlert alertWithMessageText:@"Login"
defaultButton:@"Login"
alternateButton:@"Cancel"
otherButton:nil
informativeTextWithFormat:@"Would you like to login to enable voting?"];
// Display alert
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
// Return focus to window
[[alert window] orderOut:self];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setBool:YES forKey:@"loginPrompted"];
[prefs synchronize];
if (returnCode == 1){
self.windowController = [[CCPreferencesController alloc] initWithWindowNibName:@"PreferenceMenu"];
[self.windowController showWindow:self];
}
}];
}
}
CCPreferencesController.h:
#import <Cocoa/Cocoa.h>
@interface CCPreferencesController : NSWindowController {}
...
@end
CCPreferencesController.m
#import "CCPreferencesController.h"
#import "CCAppDelegate.h"
@implementation CCPreferencesController
- (id)init{
if(self = [super initWithWindowNibName:@"PreferenceMenu"]) {}
return self;
}
- (void)awakeFromNib{
...
[self.toolbar setSelectedItemIdentifier:@"general"];
}
- (void)showWindow:(id)sender{
if ([CCAppDelegate class] == [sender class]){
[self openLogin];
}
[super showWindow:sender];
}
// This is gross
- (void)openLogin{
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 0.1);
dispatch_after(delay, dispatch_get_main_queue(), ^(void){
[self.toolbar setSelectedItemIdentifier:@"tab"];
[self.tabView selectTabViewItemAtIndex:1];
NSRect frame = [self.window frame];
frame.size.height += 55;
frame.origin.y -= 55;
[self.window setFrame:frame display:YES animate:YES];
});
}
答案 0 :(得分:1)
我不走这条路,但也许你的情况是正确的,因为你做了bloc kingly但是在你调用showWindow之前你可以强制加载窗口。在致电self.window
super showWindow
请注意,调用getter来实现副作用也远非干净!
最好将windowDidLoad等窗口中需要窗口的代码移动
@implementation MyWindowController {
NSDictionary *_options;
}
- (void)showWindow:(id)sender{
if ([CCAppDelegate class] == [sender class]){
_options = @{@"Login": @YES};
} else {
_options = nil;
}
if(self.isWindowLoaded) {
[self applyOptions];
}
[super showWindow:sender];
}
- (void)windowDidLoad {
[self applyOptions];
}
- (void)applyOptions {
if([_options[@"Login"] boolValue]) {
//if logged in ... blablabla
}
}
答案 1 :(得分:0)
如果您在[super showWindow:sender]
之前致电[self openLogin]
怎么办?像
- (void)showWindow:(id)sender {
[super showWindow:sender];
if ([sender isKindOfClass:[CCAppDelegate class]]) {
[self openLogin];
}
}
添加了:
抱歉,我建议在[super showWindow:sender]
之前移动[self openLogin]
并不能解释您的问题。 self.toolbar
和self.tabView
为nil
,因为它们是IBOutlet
,并且在加载相应的nib文件之前不会设置插座。延迟只是等待nib文件加载的一种方式,你承认自己这是一个可疑的方法。另一个解决方案是在调用[self openLogin]
之前加载nib并且我已经提议调用-showWindow:
因为-showWindow:
如果尚未加载nib则间接加载nib。我强烈建议您在-[CCPreferencesController awakeFromNib]
和-[CCPreferencesController windowDidLoad]
中添加断点。因此,您可以在调试器中查看何时加载nib文件以及何时可以使用IBOutlet
。
我对-[NSWindowController showWindow:]
在runloop上安排某些事情一无所知。不幸的是,@ Daij-Djan还没有提供有关在runloop上安排的确切内容的任何细节。 @ Daij-Djan建议在-openLogin
中执行-windowDidLoad
次操作,但我看到序列before_showWindow:, windowDidLoad, after_showWindow:
。我得出结论,nib立即加载,而不是在runloop上安排。这就是为什么在没有任何延迟的情况下致电-openLogin
是安全的原因。
我再次鼓励您在调试器中检查自己是如何加载nib文件的。最好自己检查一下,而不是依靠别人的话。
如何避免使用延迟的问题的答案:在加载窗口后调用-openLogin
,在这种情况下在-openLogin
之后调用-showWindow:
,因为它会导致nib加载。
答案 2 :(得分:0)
在我看来,看看其他选项,你应该这样做:
if (returnCode == 1){
self.windowController = [[CCPreferencesController alloc] initWithWindowNibName:@"PreferenceMenu"];
[self.windowController showWindow:self];
__weak CCAppDelegate* weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.windowController openLogin];
}
而且:
- (void)openLogin{
[self.toolbar setSelectedItemIdentifier:@"tab"];
[self.tabView selectTabViewItemAtIndex:1];
NSRect frame = [self.window frame];
frame.size.height += 55;
frame.origin.y -= 55;
[self.window setFrame:frame display:YES animate:YES];
}
你已经指示WC做某事了。如果WC提供了一种公共方法来执行此类操作,那么指示它显示特定选项卡并不是不合时宜的。
这基本上是我在用户点击通知时所做的事情。我显示窗口,让窗口控制器对下一个runloop迭代的通知中userInfo字典中的内容做出反应。
[edit] - 现在使用dispatch_async
在runloop上安排openLogin