Программно создать начальное окно приложения cocoa (OS X) - программирование
Подтвердить что ты не робот

Программно создать начальное окно приложения cocoa (OS X)

Обычно я делаю приложение iOS, но теперь я пытаюсь сделать приложение OS X, и я теряюсь в самом начале. Скажем, стиль, который я делаю, приложения iOS полностью программны, нет файлов xib или вообще, потому что у меня есть намного больше контроля, набрав, чем перетаскивая. Однако в программировании OS X он начинается с некоторых файлов xib с элементами меню и по умолчанию. В пунктах меню есть довольно много элементов, поэтому, вероятно, я не хочу, чтобы что-то мне не нравилось, но я хочу программно создать свое первое окно самостоятельно.

Итак, я сделал это:

- (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];
}

и после этого я хочу установить другие элементы в функции loadWindow: в windowController следующим образом:

- (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, поэтому я не думаю, что nibs могут справиться с этим. Я очень признателен, если кто-то может помочь. Спасибо.

А также, кто знает, как даже запускать пункты меню с нуля, программно?

Я использую последний XCode.

4b9b3361

Ответ 1

Я провел целую воскресеньку, копаясь в этой проблеме. Как и человек, задающий этот вопрос, я предпочитаю кодирование iOS и OSX без файлов nib (в основном) или Interface Builder, а также для того, чтобы идти в голом металле. Однако я использую NSConstraints. Вероятно, НЕ ДОЛЖЕН избегать IB, если вы делаете более простые пользовательские интерфейсы, однако, когда вы входите в более сложный интерфейс, становится все труднее.

Оказалось, что это довольно просто сделать, и в интересах "сообщества" я подумал, что я напишу краткий актуальный ответ здесь. Там есть некоторые более старые сообщения в блоге, и тот, который я нашел наиболее полезным, был из Lap Cat Software. "Совет O Шляпа" вам сэр!

Это предполагает ARC. Измените свой основной(), чтобы выглядеть примерно так:

#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.
}

Вы заметите, что там все еще есть Nib (xib). Это только для основного меню. Как выясняется, даже сегодня (2014), по-видимому, нет способа легко установить пункт меню позиции 0. Это тот, у кого есть название = для вашего имени приложения. Вы можете установить все, что находится справа от него, используя [NSApplication setMainMenu], но не тот. Поэтому я решил сохранить MainMenu Nib, созданный Xcode в новых проектах, и разбить его до позиции позиции 0. Я считаю, что это справедливый компромисс и с чем я могу жить. Один короткий штекер для UI Sanity... когда вы создаете меню, пожалуйста, придерживайтесь того же базового шаблона, что и другие приложения Mac OSX.

Далее измените 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. Я больше не создаю меню в коде, как показано выше. Я обнаружил, что вы можете редактировать источник MainMenu.xib в Xcode 6.1. Работает хорошо, очень гибко, и все, что требуется, - это немного экспериментировать, чтобы увидеть, как это работает. Быстрее, чем вмешиваться в код и легко локализовать! Смотрите изображение, чтобы понять, о чем я говорю:

Edit MainMenu.xib

Ответ 3

Переопределите метод -init в классе AppWindowController для создания окна, а затем вызовите метод super -initWithWindow: (который является NSWindowController обозначенным инициализатором) с этим окном.

Но я, как правило, согласен с комментариями, что нет оснований избегать NIB.

Ответ 4

Swift 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)
   }
}