2013-03-29 108 views
15

通常我正在制作iOS应用程序,但现在我正在尝试制作一个OS X应用程序,并且我一开始就迷路了。说我使iOS应用程序完全程序化的风格,没有任何xib文件,或者只是因为我通过输入而不是拖动来控制更多。但是在OS X编程中,它开始于一些带有菜单项和默认窗口的xib文件。菜单项中有相当多的项目,所以这可能不是我想要弄乱的东西,但我想以编程方式自己创建我的第一个窗口。以编程方式创建可可应用程序的初始窗口(OS X)

所以我这样做:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{  
    NSUInteger windowStyleMask = NSTitledWindowMask|NSResizableWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask; 
    NSWindow* appWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(200, 200, 1280, 720) styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:NO]; 
    appWindow.backgroundColor = [NSColor lightGrayColor]; 
    appWindow.minSize = NSMakeSize(1280, 720); 
    appWindow.title = @"Sample Window"; 
    [appWindow makeKeyAndOrderFront:self]; 
    _appWindowController = [[AppWindowController alloc] initWithWindow:appWindow]; 
    [_appWindowController showWindow:self]; 
} 

所以在这里,我创建了一个窗口,第一,并使用该windowController来初始化窗口。窗口以这种方式显示,但我只能在这里指定内部元素,如按钮和标签,但不能在windowController中指定。这让我感觉不好,所以我尝试了另一种方式。

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{ 
    _appWindowController = [[AppWindowController alloc] init]; 
    [_appWindowController showWindow:self]; 
} 

并在此之后我想设置在windowController的loadWindow:功能这样的其它元素:

- (void)loadWindow 
{ 
    [self.window setFrame:NSMakeRect(200, 200, 1280, 720) display:YES]; 
    self.window.title = @"Sample window"; 
    self.window.backgroundColor = [NSColor lightGrayColor]; 

    NSButton* sampleButton = [[NSButton alloc] initWithFrame:NSRectFromCGRect(CGRectMake(100, 100, 200, 23))]; 
    sampleButton.title = @"Sample Button!"; 
    [sampleButton setButtonType:NSMomentaryLightButton]; 
    [sampleButton setBezelStyle:NSRoundedBezelStyle]; 
    [self.window.contentView addSubview:sampleButton]; 
    NSLog(@"Loaded window!"); 
    [self.window makeKeyAndOrderFront:nil]; 
} 

不幸的是,这永远不会奏效。 loadWindow:永远不会被调用,也不windowDidLoad:。他们去了哪里?

请不要问我为什么不使用笔尖。我希望在内部制作一些高度自定义的视图,可能是OpenGL,所以我不认为nib可以处理它。如果有人能提供帮助,我将不胜感激。谢谢。

此外,谁知道如何从头开始菜单项,编程?

我正在使用最新的XCode。

+2

你的想法,XIBs无法处理OpenGL是扁平化的错误。 Apple提供了一个在XIB中使用的OpenGL视图。 XIB可以处理任何以编程方式创建的视图,它们很简单,就是封装这些项目的一种方式。 – sosborn

+0

这适合某些情况,但请考虑iOS应用中的棋盘游戏。每个棋子都是带有自定义图像的视图,那么您需要移动它们。如果窗口中的大部分视图都是高度动态的,并且带有动画,很难想象如何在不大幅混乱的情况下实现它。 – wlicpsc

+0

如果你正在使用OpenGL,你有一个视图和所有的部分,以及OpenGL中没有完成的部分。如果您在OpenGL中没有这样做,则适用相同的想法,您可以在您的XIB中拥有一个超级视图,然后您可以编程方式使用其他视图填充该视图。通过这种方式,您可以获得使用XIB的好处,同时仍然可以通过代码控制游戏板。 – sosborn

回答

37

我花了整个星期天自己挖掘这个问题。就像询问问题的人一样,我更喜欢使用没有nib文件(主要是)或Interface Builder的iOS和OSX编码,并使用裸机。尽管我使用NSConstraints。如果你在做更简单的用户界面,这可能不是值得的,但是当你进入更复杂的用户界面时,这会变得更加困难。

事实证明,这样做相当简单,为了“社区”的利益,我想我会在这里发布简要的最新答案。这里有一些较旧的博客文章,我发现最有用的博客文章是从Lap Cat Software。 '提示O帽子'给你,先生!

这个假设ARC。修改您的main()看起来是这样的:

#import <Cocoa/Cocoa.h> 
#import "AppDelegate.h" 

int main(int argc, const char *argv[]) 
{ 
    NSArray *tl; 
    NSApplication *application = [NSApplication sharedApplication]; 
    [[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:application topLevelObjects:&tl]; 

    AppDelegate *applicationDelegate = [[AppDelegate alloc] init];  // Instantiate App delegate 
    [application setDelegate:applicationDelegate];      // Assign delegate to the NSApplication 
    [application run];             // Call the Apps Run method 

    return 0;  // App Never gets here. 
} 

您会注意到,仍有笔尖(厦门国际银行)在那里。这仅适用于主菜单。事实证明,即使在今天(2014年),显然也无法轻易设置位置0菜单项。这是一个标题=你的应用程序名称。您可以使用[NSApplication setMainMenu]来设置它的所有内容,但不是那个。所以我选择将由Xcode创建的MainMenu Nib保留在新项目中,并将其剥离到位置0项目。我认为这是一种公平的妥协方式,也是我可以忍受的。 UI Sanity的一个简单插件......当您创建菜单时,请遵循与其他Mac OSX Apps相同的基本模式。

下一页修改AppDelegate中看起来是这样的:

-(id)init 
{ 
    if(self = [super init]) { 
     NSRect contentSize = NSMakeRect(500.0, 500.0, 1000.0, 1000.0); 
     NSUInteger windowStyleMask = NSTitledWindowMask | NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; 
     window = [[NSWindow alloc] initWithContentRect:contentSize styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:YES]; 
     window.backgroundColor = [NSColor whiteColor]; 
     window.title = @"MyBareMetalApp"; 

     // Setup Preference Menu Action/Target on MainMenu 
     NSMenu *mm = [NSApp mainMenu]; 
     NSMenuItem *myBareMetalAppItem = [mm itemAtIndex:0]; 
     NSMenu *subMenu = [myBareMetalAppItem submenu]; 
     NSMenuItem *prefMenu = [subMenu itemWithTag:100]; 
     prefMenu.target = self; 
     prefMenu.action = @selector(showPreferencesMenu:); 

     // Create a view 
     view = [[NSTabView alloc] initWithFrame:CGRectMake(0, 0, 700, 700)]; 
    } 
    return self; 
} 

-(IBAction)showPreferencesMenu:(id)sender 
{ 
    [NSApp runModalForWindow:[[PreferencesWindow alloc] initWithAppFrame:window.frame]]; 
} 

-(void)applicationWillFinishLaunching:(NSNotification *)notification 
{ 
    [window setContentView:view];   // Hook the view up to the window 
} 

-(void)applicationDidFinishLaunching:(NSNotification *)notification 
{ 
    [window makeKeyAndOrderFront:self];  // Show the window 
} 

宾果......你是好去!你可以从AppDelegate那里开始工作,就像你熟悉的一样。希望有所帮助!

UPDATE:我不再在代码中创建菜单,如上所示。我发现您可以在Xcode 6.1中编辑MainMenu.xib源代码。工作很好,非常灵活,只需要一点实验就可以了解它的工作原理。比在代码中搞乱并易于本地化更快!看图片来了解我是什么关于:

Edit MainMenu.xib

+3

经过这么长时间,非常感谢您的详细解释。我认为几个月后没有人会关心这一点,但我改变了这个问题的正确答案并赞扬你的工作。非常感谢。 – wlicpsc

+1

谢谢!这很棒。 –

+0

如何找到相当于'MainMenu'的笔尖...我很困惑?什么是笔尖?哈 – maxisme

0

覆盖的-init方法在AppWindowController类来创建窗口,然后调用super-initWithWindow:方法(这是NSWindowController的指定初始化)与该窗口。

但我普遍赞同那些没有理由避免NIB的意见。

+0

谢谢。虽然,看起来每个人都很难抵制不使用笔尖。对于iOS,我曾经这样做过,但它并不完整,就像故事板一样,塞格只能做一堆有限的东西,所以我完全放弃了,转而使用纯代码。这与OS X不一样吗? – wlicpsc

+0

我还没有做iOS开发,但我明白故事板和segues强加的结构比NIBs更多。 NIB只是可以随意重构的“冻干”对象图。这并不是说你最终在NIB中做了所有事情,并且只有很少或没有代码。只是使用NIB不会影响你使用代码做什么,并且减轻了一些繁琐而繁琐的代码,所以为什么不呢? –

+0

故事板与XIB不同,是的,它们的方式更有限。就像我在其他评论中所说的那样,XIB只是封装视图/对象的简单方法。他们并没有真的做别的。它们不是视图的替代品,它们不是控制器的替代品。他们只是为了让生活更轻松。你可以自由使用它们,但是你最终会写更多的代码来完成相同的事情。 – sosborn

0

斯威夫特4:

// File main.swift 
autoreleasepool { 
    // Even if we loading application manually we need to setup `Info.plist` key: 
    // <key>NSPrincipalClass</key> 
    // <string>NSApplication</string> 
    // Otherwise Application will be loaded in `low resolution` mode. 
    let app = Application.shared 
    app.setActivationPolicy(.regular) 
    app.run() 
} 

// File Application.swift 
public class Application: NSApplication { 

    private lazy var mainWindowController = MainWindowController() 
    private lazy var mainAppMenu = MainMenu() 

    override init() { 
     super.init() 
     delegate = self 
     mainMenu = mainAppMenu 
    } 

    public required init?(coder: NSCoder) { 
     super.init(coder: coder) // This will newer called. 
    } 

} 

extension Application: NSApplicationDelegate { 

    public func applicationDidFinishLaunching(_ aNotification: Notification) { 
     mainWindowController.showWindow(nil) 
    } 
} 
相关问题