I've written a corevideo application which has one window with a single content view.
The window resizes as expected. I've added the code to make it accept the fullscreen event, which the window does and works as expected, the dock and menu autohide and appear when the mouse hovers in the expected places.
However when I come out of fullscreen mode, I get an assertion failure in the AppKit's NSWindow_FullScreen.m which I cannot find mentioned anywhere in the fullscreen documentation nor can I find the error message searching google.
I've tried adding an observer for the NSWindowDidExitFullScreen Notification but the assertion remains.
I'm hoping someone can help.
2020-05-10 10:01:16.812 a.out[45616:2858300] *** Assertion failure in -[NSWindow _didExitFullScreen], /BuildRoot/Library/Caches/com.apple.xbs/Sources/AppKit/AppKit-1561.61.100/FullScreen.subproj/NSWindow_FullScreen.m:469
2020-05-10 10:01:16.812 a.out[45616:2858300] content controller was not cleaned up properly
I'm not sure what it's referring to as the Content Controller, I've tried adding a window controller but it still fails, I'm not sure what needs to be cleaned up, as my application is still running and rendering in the window.
Here's my minimal app which exhibits the issue. Compile with: gcc -framework AppKit example.m
#import <AppKit/AppKit.h>
int main (int argc, char **argv)
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
NSUInteger windowStyle = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable;
NSRect wr = NSMakeRect(0,0,640,480);
NSWindow * window = [[NSWindow alloc] initWithContentRect:wr
styleMask:windowStyle
backing:NSBackingStoreBuffered
defer:NO];
[window autorelease];
NSWindowCollectionBehavior behavior = [window collectionBehavior];
behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
[window setCollectionBehavior:behavior];
[[NSNotificationCenter defaultCenter]
addObserver:NSApp
selector:#selector(terminate:)
name:NSWindowWillCloseNotification
object:nil];
[window orderFrontRegardless];
[NSApp run];
[pool drain];
return 0;
}
The cause of this issue is that a NSWindowWillCloseNotification is sent when the window exits fullscreen, removing the lines:
[[NSNotificationCenter defaultCenter]
addObserver:NSApp
selector:#selector(terminate:)
name:NSWindowWillCloseNotification
object:nil];
And handling quit events differently resolved this issue.
Related
I created a MPMoviePlayerViewController which plays a live video. However, if I play the video twice meaning opening the player, clicking done, and playing the stream again. The result is only a black screen with no controls of the MPMoviePlayerViewController. And I need to stop the simulator cause I think the application is crashing. Here's how I did it
- (void) playUrl:(NSURL *)movieInfo
{
NSURL *streamUrl = movieInfo;
MPMoviePlayerViewController *mpvc = [[MPMoviePlayerViewController alloc] initWithContentURL:streamUrl];
[[mpvc view] setFrame:self.view.bounds];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
mpvc.moviePlayer.movieSourceType = MPMovieSourceTypeStreaming;
[mpvc.moviePlayer setControlStyle:MPMovieControlStyleFullscreen];
[mpvc.moviePlayer setShouldAutoplay:YES];
[mpvc.moviePlayer setFullscreen:NO animated:YES];
[mpvc setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[mpvc.moviePlayer setScalingMode:MPMovieScalingModeNone];
[mpvc.moviePlayer setUseApplicationAudioSession:NO];
[self presentMoviePlayerViewControllerAnimated:mpvc];
}
- (void) movieFinishedCallback:(NSNotification*) aNotification
{
MPMoviePlayerController *player = [aNotification object];
[player stop];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:player];
[player.view removeFromSuperview];
NSLog(#"stopped?");
}
I see that in your movieFinishedCallback: implementation, you remove the MPMoviePlayerController view, but in your playUrl: implementation, you are only setting the view's frame, presumably after you have already added the view in viewDidLoad.
One obvious change which is worth trying, is update you code to use the AVPictureInPictureController or AVPlayerViewController class from the AVKit framework, or the WKWebView class from WebKit. According to the MPMoviePlayerViewController docs, it is deprecated as of iOS 9:
The MPMoviePlayerViewController class is formally deprecated in iOS 9. (The MPMoviePlayerController class is also formally deprecated.) To play video content in iOS 9 and later, instead use the AVPictureInPictureController or AVPlayerViewController class from the AVKit framework, or the WKWebView class from WebKit.
Try moving the line where you add the view to the hierarchy, to the playUrl: method. Generally, it is good practice to have countering implementations in opposing methods for your event counterparts. For instance, implement a method to build and add a view when an event starts, and have a corresponding method where you tear down and remove the same view when the same event ends. But, I say 'generally' because there are always exceptions, and you may have very compelling reasons for not doing so. So, in this case, the opposing calls are presentMoviePlayerViewControllerAnimated: and dismissMoviePlayerViewControllerAnimated:, available from the UIViewController category.
After changing the view access to using dot-notation, to be consistent with your callback implementation, here is what your new playUrl: implemntation would look like, assuming you're adding the view to self.view:
- (void) playUrl:(NSURL *)movieInfo
{
NSURL *streamUrl = movieInfo;
MPMoviePlayerViewController *mpvc = [[MPMoviePlayerViewController alloc] initWithContentURL:streamUrl];
[mpvc.view setFrame:self.view.bounds];
[self.view addSubview:mpvc.view];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(movieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
mpvc.moviePlayer.movieSourceType = MPMovieSourceTypeStreaming;
[mpvc.moviePlayer setControlStyle:MPMovieControlStyleFullscreen];
[mpvc.moviePlayer setShouldAutoplay:YES];
[mpvc.moviePlayer setFullscreen:NO animated:YES];
[mpvc setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[mpvc.moviePlayer setScalingMode:MPMovieScalingModeNone];
[mpvc.moviePlayer setUseApplicationAudioSession:NO];
[self presentMoviePlayerViewControllerAnimated:mpvc];
}
Another option is to simply not remove the player's view in your callback method. If that is not the culprit, then the next thing I would investigate is check if you are sending messages to nil objects. Also, see what happens when you take out all the implementation from movieFinishedCallback:, except for getting and stopping the player.
I hope that helps!
Fixed the issue by removing the [player.view removeFromSuperview] line
(Sorry in advance for the seemingly large amount of code here) I'm trying to create a window with an OpenGL context with Cocoa, but I'm finding that I am unable to set the view property of the NSOpenGLContext that I create.
I cannot simply use an NSOpenGLView, since I need to interface with a C++ drawing backend and use multiple contexts. The code I post here is just me trying to get to grips with handling NSOpenGLContexts, but it will be used in a much larger project. This is why I'm instantiating NSApplication and my NSWindow manually rather than through a NIB/NSApplicationMain.
My main.m file:
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#import "Delegate.h"
int main(int argc, const char * argv[]) {
[NSApplication sharedApplication];
Delegate* dlg = [[Delegate alloc] init];
[NSApp setDelegate:dlg];
[NSApp run];
return 0;
}
I then have my delegate class, and I'll refrain from posting the file Delegate.h since it'll be pretty obvious what's there given these contents of Delegate.m:
#import <Cocoa/Cocoa.h>
#import "Delegate.h"
#import <OpenGL/gl.h>
#implementation Delegate
- (void) draw
{
[self.glContext makeCurrentContext];
glClearColor(1, 0, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
[self.glContext flushBuffer];
}
- (void) applicationDidFinishLaunching:(NSNotification *)notification
{
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
self.win = [[NSWindow alloc] initWithContentRect:NSMakeRect(30, 30, 300, 200)
styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask
backing:NSBackingStoreBuffered
defer:YES];
NSOpenGLPixelFormatAttribute glAttributes[] =
{
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
0
};
self.glContext = [[NSOpenGLContext alloc] initWithFormat:[[NSOpenGLPixelFormat alloc] initWithAttributes:glAttributes]
shareContext:nil];
[self.glContext setView: [self.win contentView]];
printf("view:%p, contentView:%p\n", [self.glContext view], [self.win contentView]);
[self.win makeKeyAndOrderFront:nil];
[NSTimer
scheduledTimerWithTimeInterval:.1
target:self
selector:#selector(draw)
userInfo:nil
repeats:YES];
}
The window opens just fine. I am able to tell that -applicationDidFinishLaunching and -draw are being called. The window shows up empty however.
The printf call shows that the view property of self.glContext is equal to the address 0x0. I see no documentation or other forum threads about why I would be unable to set the drawable object of an NSOpenGLContext.
I have tried putting the NSOpenGLContext in its own subclass of NSView and adding that subclass as a subview of the window's content view, but with no success.
Try setting the defer parameter of -[NSWindow initWithContentRect:...] to NO. You may also wish to set the GL context's view after ordering the window on-screen.
Basically, -[NSOpenGLContext setView:] can fail if the view's window doesn't yet have a "device". When that's happened to me, it usually logs a message to the console about an "invalid drawable", but I haven't checked in recent versions of the OS.
Also, you need to register as an observer of the NSViewGlobalFrameDidChangeNotification notification from the view and, in response, call -update on the GL context object.
My code below crashes if I have the code in windowWillClose: that releases
my MyWindowController, otherwise it works fine.
I test it on Mac OS 10.6.8.
I am using XCode 3.1.3.
What have I done wrong?
It seems like the window is not disposed of before I release MyWindowController,
because it crashes in a NSTableView method.
My button handler calls [NSApp stopModalWithCode:0];
MyDialog()
{
MyWindowController* controller = [[MyWindowController alloc] init];
[controller showWindow:controller];
NSWindow* window = [controller window];
[NSApp runModalForWindow:window];
[window close];
}
In my MyWindowController:
- (void)windowWillClose:(NSNotification*)notification
{
[self autorelease];
}
You are releasing 'self' in windowWillClose - that seems wrong.
Surely anything like that should be done in dealloc?
-(void)dealloc
{
[super dealloc];
}
Also, you might be better autoreleasing controller when it is initially alloc'd?
I trying to show video on my App. The App hides the iPhone top panel. The player seems to work fine. There is just one annoying problem: when the player shows the video, it sometimes show the top panel and sometime hides it. When it is hidden, the video player panel is pushed a little (the same size of the panel that used to be there). Is is Apple bug? Am I doing something wrong?
Here is my code:
- (void) showFullscreenMediaWithURL: (NSURL *) mediaURL
{
MPMoviePlayerViewController *ctrl = [[MPMoviePlayerViewController alloc] initWithContentURL: mediaURL];
[[NSNotificationCenter defaultCenter] addObserver:self selector: #selector(playbackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:ctrl.moviePlayer];
ctrl.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
ctrl.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[ctrl setWantsFullScreenLayout:YES];
[self presentMoviePlayerViewControllerAnimated:ctrl];
[ctrl release];
}
-(void) playbackDidFinish:(NSNotification*)aNotification
{
NSLog(#"Finished playback");
MPMoviePlayerController *player = [aNotification object];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification
object:player];
[player stop];
[self dismissMoviePlayerViewControllerAnimated];
[[captureManager session] startRunning];
}
if by iPhone top panel you mean the iPhone Status bar, then the solution should be simple.
Just before present/dismissMoviePlayerViewControllerAnimated add the following:
// Hide Status Bar
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
// Show Status Bar
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
Update: I can see what seems to be your problem.
First, the upper bar with the network indication icons and other information is the status bar (and nothing else). Your problem seems to be more ViewController related then a MediaPlayer. In other words, If you would have try to "push" some other ViewController to full screen (as the player is) you would have experience the exact same issue.
Second, the proper way, or I might say: my preferred way, of loading a view controller to full screen is the following:
Setup a full screen rootViewController which will be loaded on applicationDidFinishLaunchingWithOptions on your appDelegate.
On the rootViewController init put your default viewController (the one you used to load from appDelegate). Make sure that the rootViewController.view's frame is filling the screen.
Create 2 messages on rootViewController: LoadFullscreen:viewController and dismissFullscreen using present/dismissModelViewController. the setStatusBarHidden messages should be called from here.
To Lunch the player on full screen, create the player viewController and perform [rootViewController LoadFullscreen:player];
There are some other ways, but generally, this is the best practice and the method I recommend. It's relatively "a lot of code" to implement, thats why I couldn't help you with code snippers, but the general idea is relatively simple.
I hope that's help, E.G :)
I found out how to create a window in Cocoa programmatically but can't figure out how to react to events. The window is not reacting to a Quit request or button click.
I tried adding the following controller and used setDelegate/setTarget without luck:
#interface AppController : NSObject {
}
- (IBAction)doSomething:(id)sender;
#end
#implementation AppController
- (IBAction)doSomething:(id)sender;
{
printf("Button clicked!\n");
}
#end
int main(int argc, char **args){
NSRect frame = NSMakeRect(0, 0, 200, 200);
AppController *controller = [[AppController alloc] init];
> [[NSApplication sharedApplication] setDelegate:controller];
NSWindow* window = [[NSWindow alloc] initWithContentRect:frame
styleMask:NSBorderlessWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask
backing:NSBackingStoreBuffered
defer:NO];
[window setBackgroundColor:[NSColor blueColor]];
NSButton *button = [ [ NSButton alloc ] initWithFrame: NSMakeRect( 30.0, 20.0, 80.0, 50.0 ) ];
[ button setBezelStyle:NSRoundedBezelStyle];
[ button setTitle: #"Click" ];
> [ button setAction:#selector(doSomething:)];
> [ button setTarget:controller];
[ [ window contentView ] addSubview: button ];
[window makeKeyAndOrderFront:NSApp];
[[NSRunLoop currentRunLoop] run];
return 0;
}
You need to invoke -[NSApplication run] instead of -[[NSRunLoop currentRunLoop] run]. The reason should be clear if you look at the basic structure of the method:
- (void)run
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self finishLaunching];
shouldKeepRunning = YES;
do
{
[pool release];
pool = [[NSAutoreleasePool alloc] init];
NSEvent *event =
[self
nextEventMatchingMask:NSAnyEventMask
untilDate:[NSDate distantFuture]
inMode:NSDefaultRunLoopMode
dequeue:YES];
[self sendEvent:event];
[self updateWindows];
} while (shouldKeepRunning);
[pool release];
}
NSApplication encapsulates a lot about how to get an event, how to dispatch them and how to update windows.
I found out how to create a window in Cocoa programmatically …
Why? Why not just make a nib?
The window is not reacting to a Quit request or button click.
How would you quit a window? This isn't Windows 3; applications can have multiple windows on Mac OS X. As such, closing a window and quitting an application are separate actions.
[[NSRunLoop currentRunLoop] run];
Except in rare circumstances, running the run loop is NSApplication's job, and you should leave that to it. Use NSApplicationMain or -[NSApplication run] to tell the application to run.
Excellent question. I think Matt Gallagher answered it already, but if you want to go further with this, you'll have to delve into Apple's event-handling documentation. Bear in mind that doing everything programmatically will require a solid understanding of cocoa fundamentals.
I spent an entire day looking for answers to the GUI and Menu portion of this question. There are not that many current, concise answers out there to the question. So after solving it for myself, I posted an answer which addresses this on Stack here: Cocoa GUI Programmatically. I add a referral to it here to help community members who are digging around for the same answers.