2013-03-29 9 views
15

Solitamente sto creando app per iOS ma ora sto provando a creare un'app per OS X e mi sento perso all'inizio. Dì lo stile che faccio che le app iOS siano totalmente programmatiche, non ci sono file xib o qualsiasi altro solo perché ho un controllo molto maggiore digitando piuttosto che trascinando. Tuttavia, nella programmazione OS X, inizia con alcuni file xib con le voci di menu e una finestra predefinita. Ci sono un sacco di elementi nelle voci del menu, quindi probabilmente non è qualcosa che voglio gironzolare, ma voglio creare a livello di codice la mia prima finestra da solo.crea in modo programmatico la finestra iniziale dell'app cacao (OS X)

Quindi ho fatto questo:

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

Così qui, ho creato una finestra prima, e l'uso che windowController a init questa finestra. La finestra si presenta in questo modo, ma posso solo specificare gli elementi interni, come pulsanti ed etichette qui, ma non nella finestra Controllo. Mi fa sentire male, quindi ho provato un altro modo.

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

e dopo questo voglio impostare gli altri elementi nella funzione loadWindow: nel windowController come questo:

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

Purtroppo, questo non funziona mai. il numero loadWindow: non viene mai chiamato, né windowDidLoad:. Dove sono andati?

E per favore non chiedere perché non uso pennini. Vorrei creare alcune viste altamente personalizzate all'interno, possibilmente OpenGL, quindi non credo che i pennini possano gestirle. Sono molto apprezzato se qualcuno potesse aiutare. Grazie.

E inoltre, chi sa come avviare le voci del menu da zero, a livello di programmazione?

Sto usando l'ultimo XCode.

+2

La tua idea che XIBs non può gestire OpenGL è completamente sbagliato. Apple fornisce una vista OpenGL da utilizzare in XIBs. Gli XIB possono gestire tutto ciò che è possibile creare viste programmaticamente, sono un modo semplice per incapsulare quegli elementi. – sosborn

+0

Ciò si adatta in alcuni casi, ma pensa ai giochi da tavolo nelle app iOS. ogni pezzo degli scacchi è una vista con immagini personalizzate, quindi è necessario spostarli. Se la maggior parte delle visualizzazioni nelle finestre è altamente dinamica, con animazioni, è difficile immaginare come potrebbe essere implementata senza creare grossi problemi. – wlicpsc

+0

Se lo stai facendo OpenGL, hai una vista e tutti i pezzi e cosa non viene fatto in OpenGL. Se non lo fai in OpenGL, si applica la stessa idea, puoi avere una superview in te XIB e poi puoi popolare quella vista con le altre viste in modo programmatico. In questo modo si ottengono i vantaggi dell'utilizzo di un XIB mentre si controlla ancora la scheda di gioco in codice. – sosborn

risposta

37

Ho passato un'intera domenica a scavare questo problema da solo. Come la persona che fa la domanda, preferisco codificare iOS e OSX senza file di pennini (principalmente) o Interface Builder e andare in bare metal. Io però uso NSConstraints. Probabilmente NON vale la pena di evitare IB se stai facendo UI più semplici, ma quando entri in un'interfaccia utente più complessa diventa più difficile.

Risulta essere abbastanza semplice da fare, e per il beneficio della "Comunità" ho pensato di pubblicare una risposta concisa aggiornata qui. Ci sono alcuni post dei blog più vecchi là fuori e quello che ho trovato più utile sono stati quelli da Lap Cat Software. 'Suggerimento O The Hat' a voi signore!

Questo presuppone ARC. Modificare la main() per cercare qualcosa di simile:

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

Avrete notato che c'è ancora un pennino (XI ter) in là. Questo è solo per il menu principale. Come risulta anche oggi (2014) apparentemente non c'è modo di impostare facilmente la voce di menu posizione 0. Questo è quello con il titolo = al nome della tua app. Puoi impostare tutto a destra usando [NSApplication setMainMenu] ma non quello.Quindi ho deciso di mantenere il pennino MainMenu creato da Xcode in nuovi progetti e di ridurlo solo alla posizione 0. Penso che sia un buon compromesso e qualcosa con cui posso convivere. Un breve plug-in per l'interfaccia utente Sanity ... durante la creazione dei menu, seguire lo stesso schema di base delle altre app Mac OSX.

Avanti modificare l'AppDelegate a cercare qualcosa di simile:

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

e bingo ... siete pronti per partire! Puoi iniziare a lavorare da lì in AppDelegate praticamente come se avessi familiarità con. Spero possa aiutare!

UPDATE: Non creo più i menu in codice come ho mostrato sopra. Ho scoperto che puoi modificare la sorgente MainMenu.xib in Xcode 6.1. Funziona bene, molto flessibile e tutto ciò che serve è un po 'di sperimentazione per vedere come funziona. Più veloce del caos nel codice e facile da localizzare! Vedere le foto per capire quello che sto su circa:

Edit MainMenu.xib

+3

Grazie mille per questa spiegazione dettagliata dopo tanto tempo. Pensavo che nessuno si sarebbe preso cura di questo dopo mesi, ma cambio la risposta corretta a questa e raccomando il tuo lavoro. Grazie mille ancora. – wlicpsc

+1

Grazie! È grandioso –

+0

Come trovo il mio equivalente del pennino "MainMenu" ... Sono così confuso? Cos'è un pennino? !!! Ha – maxisme

0

l'override del metodo -init nella classe AppWindowController per creare la finestra e quindi chiamare super 'metodo s -initWithWindow: (che è NSWindowController' s di inizializzazione designato) con quella finestra.

Ma in generale sono d'accordo con i commenti che ci sono pochi motivi per evitare i NIB.

+0

Grazie. Tuttavia, sembra che tutti stiano resistendo senza usare pennini. Per iOS, lo facevo, ma era incompleto, come lo storyboard ei segues possono fare solo un mucchio di cose limitate, quindi ho completamente rinunciato e sono passato al puro codice. Non è lo stesso per OS X? – wlicpsc

+0

Non ho ancora sviluppato iOS, ma capisco che gli storyboard e i folletti impongono una struttura molto più complessa rispetto ai NIB. I NIB sono solo grafici di oggetti "congelati" che è possibile ricostituire a piacere. Non è che finisci per fare tutto in una NIB e hai poco o nessun codice.È solo che l'uso di NIB non interferisce con il fare ciò che si ha a che fare con il codice e allevia alcuni dei noiosi e gravosi codici, quindi perché no? –

+0

Gli storyboard sono diversi dagli XIB e sì, sono molto più limitati. Come ho detto nel mio altro commento, gli XIB sono semplicemente un modo semplice per incapsulare le tue viste/oggetti. Non fanno davvero altro. Non sostituiscono le visualizzazioni e non sostituiscono i controller. Sono semplicemente lì per semplificare la vita. Sei libero di non usarli, ma finirai per scrivere altro codice per fare le stesse cose. – sosborn

0

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) 
    } 
} 
Problemi correlati