2013-03-29 112 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) 
    } 
} 
相關問題