窗口排序和XIB

时间:2012-10-30 15:51:20

标签: objective-c xcode macos cocoa

我正在设置OSX开发的第一步,我遇到了一些问题。我对iOS开发有很多经验,但OSX程序的窗口系统是另外的。

我正在建立一个像twitter这样的社交网络的客户端,需要2个单独的窗口控制器才能首次启动应用程序,如果您已登录以显示时间线,则需要一个,如果您尚未登录,则需要登录。在info.plist中,您需要为它提供一个main.xib。为此,我制作了一个我隐藏的空xib,第二个应用程序启动。这对IMO来说不是一个好的解决方案,对此有什么更好的解决方案?我想保持窗口与appdelegate分开,因为这样我可以保持我的代码分离。

这给我一个问题,当我打开我的“第二个”窗口进行登录时,它会显示但不活动。我已经尝试了所有的东西,例如orderFront:,activateIgnoringOtherApps:,makeKeyAndOrderFront:&更多。但这一切都行不通。

所以:首先,是否有更好的方法来处理info.plist中需要的main.xib,如果没有,是否有解决焦点问题的方法? 我正在使用osx 10.7

2 个答案:

答案 0 :(得分:2)

对于多次一次性,你真的应该将你的app delegate与你的窗口控制器分开。继续,从模板中创建一个新的Cocoa应用程序。在MainMenu.xib中,删除该窗口。在AppDelegate.h中删除IBOutletNSWindow。创建一些NSWindowController的新子类,其中包含XIB s - 可能LoginWindowControllerTimelineWindowController

对于“最终”NSWindowController子类(即那些不会被子类化的子类),指定初始化器的最佳实践是

//for our example class LoginWindowController
- (id)init
{
    self = [super initWithWindowNibName:@"LoginWindowController"];
    if (self) {
        //....
    }
    return self;
}

现在,在您的app委托中,您应该为两个不同的窗口控制器实例@properties

//Within AppDelegate.m

#import "AppDelegate.h"
#import "LoginWindowController.h"
#import "TimelineWindowController.h"

@interface AppDelegate ()
@property (nonatomic) LoginWindowController *loginWindowController;
@property (nonatomic) TimelineWindowController *timelineWindowController;
//For the sake of this demo, add a property for the loggedIn state:
@property (nonatomic) BOOL loggedIn;
@end

您应该在app委托中使用某种方法来呈现正确的窗口控制器。我们称之为-updateWindowVisibility

- (void)updateWindowVisibility
{
    BOOL isLoggedIn = self.loggedIn;

    BOOL loginWindowVisible = self.loginWindowController.window.isVisible;
    BOOL showLoginWindow = !isLoggedIn;

    BOOL timelineWindowVisible = self.timelineWindowController.window.isVisible;
    BOOL showTimelineWindow = isLoggedIn;

    if (!loginWindowVisible && showLoginWindow) {
        if (!self.loginWindowController) self.loginWindowController = [[LoginWindowController alloc] init];
        [self.loginWindowController showWindow:nil];
    } else if (loginWindowVisible && !showLoginWindow) {
        [self.loginWindowController close];
        self.loginWindowController = nil;
    }

    if (!timelineWindowVisible && showTimelineWindow) {
        if (!self.timelineWindowController) self.timelineWindowController = [[TimelineWindowController alloc] init];
        [self.timelineWindowController showWindow:nil];
    } else if (timelineWindowVisible && !showTimelineWindow) {
        [self.timelineWindowController close];
        self.timelineWindowController = nil;
    }
}

上面实现的这种方法比目前的设置需要更多的工作,但是当你需要显示/隐藏其他窗口时应该更容易修改。此时剩下要做的就是从-updateWindowVisibility拨打-applicationDidFinishLaunching:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    self.isLoggedIn = NO;
    [self updateWindowVisibility];
}

我发布了一个example app to github来演示这种方法。

答案 1 :(得分:1)

在结构方面(你的第一个问题),我建议这样做:

  1. 使用一个空白窗口和一个覆盖它的大型NSView(称为megaView)创建一个XIB。在AppDelegate中为您的大NSView创建一个IBOutlet。将应用程序设置为在加载时使用此XIB。
  2. 创建两个单独的NSView XIB:一个用于登录状态,一个用于注销状态。把你的布局放在这些。
  3. 创建两个NSViewController子类:一个控制您刚刚创建的每个NSView的逻辑。我们称之为 LoggedOutViewController LoggedInViewController
  4. 跳回您创建的两个NSView。将登录的NSView的文件所有者设置为LoggedInViewController,将已注销的NSView的文件所有者设置为LoggedOutViewController。将每个文件所有者的视图(右键单击文件所有者以查找它)连接到相应的NSView。
  5. 在您的app delegate中,以您需要的任何方式确定用户的身份验证状态。
  6. 如果已登录,请执行以下操作:

    NSViewController *loggedInController = [[NSViewController alloc] initWithNibName:@"NibNameGoesHere" bundle:nil];
    [[self megaView] addSubview:[loggedInController view]];
    

    否则使用您的loggedOutController进行上述过程:

    NSViewController *loggedOutController = [[NSViewController alloc] initWithNibName:@"OtherNibNameGoesHere" bundle:nil];
    [[self megaView] addSubview:[loggedOutController view]];
    

    这应该可以让你得到你想要的东西,并且可能会在这个过程中清理你的第二个问题。我的答案与Nate的区别在于我的使用相同的窗口。有条件地实例化视图控制器并将其视图加载到超视图中可能是我学到的Cocoa最重要的方面。