I am racking my brain on this one. I've been trying to duplicate the initial metal project provided by Apple but without using interface builder at all. I'm able to create a window, but it's blank, nothing is rendering and I can't figure out why.
main.m
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSApplication* app = [NSApplication sharedApplication];
AppDelegate* appDelegate = [[AppDelegate alloc] init];
[app setDelegate:appDelegate];
[app run];
}
return EXIT_SUCCESS;
}
AppDelegate.h
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
}
@end
AppDelegate.m
#import <Metal/Metal.h>
#import "AppDelegate.h"
#import "GameViewController.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (id)init {
if (self = [super init]) {
NSScreen* mainScreen = [NSScreen mainScreen];
NSRect frame = NSMakeRect(0, 0, mainScreen.frame.size.width / 2, mainScreen.frame.size.height / 2);
NSUInteger styleMask =
NSWindowStyleMaskTitled |
NSWindowStyleMaskResizable |
NSWindowStyleMaskClosable |
NSWindowStyleMaskMiniaturizable;
NSBackingStoreType backing = NSBackingStoreBuffered;
window = [[NSWindow alloc] initWithContentRect:frame styleMask:styleMask backing:backing defer:YES];
[window center];
GameViewController* gvc = [[GameViewController alloc] init];
MTKView* metalView = [[MTKView alloc] initWithFrame:frame device:MTLCreateSystemDefaultDevice()];
[metalView setClearColor:MTLClearColorMake(0, 0, 0, 1)];
[metalView setColorPixelFormat:MTLPixelFormatBGRA8Unorm];
[metalView setDepthStencilPixelFormat:MTLPixelFormatDepth32Float];
[metalView setDelegate:gvc];
[gvc setView:metalView];
[window setContentView:metalView];
//[window setContentViewController: gvc];
}
return self;
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
[window makeKeyAndOrderFront:self];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
return YES;
}
@end
The other files; GameViewController.h
, GameViewController.m
, Shaders.metal
, SharedStructures.h
; are the same as what XCode 8.2.1 (8C1002) auto generates when you create a Game project that uses Metal with Objective-c.
You'll notice the line [window setContentViewController: gvc];
is commented out. When this is uncommented I get an EXEC_BAD_ACCESS
on the first line of GameViewController.m
's render function dispatch_semaphore_wait(_inflight_semaphore, DISPATCH_TIME_FOREVER);
Clearly there's something that I'm missing, but I've been googling all day and I can't seem to figure it out.
The issue is that, when an
NSViewController
such as theGameViewController
is not loaded from a NIB, it doesn't call the-viewDidLoad
method. TheGameViewController
does important work in that method.You can move the creation of the view into an override of
-loadView
inGameViewController
. Create the view and assign it toself.view
. You don't need to set its delegate. The existingGameViewController
code does that already. Setting the view's properties should be moved to-_setupView
where the existing code already does that sort of thing.Don't create the view in
-[AppDelegate init]
. Obviously, you also won't be able to set the view controller's view there, either. Just set the view controller as the window's content view controller (uncomment that line) and the rest will be taken care of automatically. When the window requests the content view controller'sview
property, it will call your override of-loadView
along with the existing-viewDidLoad
, etc.