NSWindowController not hiding its window at init? - objective-c

I have an NSDocument subclass with two NSWindowControllers corresponding to 2 different xib.
Following the Document-Based Application Guide I have added the following in my document.m implementation
- (void)makeWindowControllers
{
NSLog(#"in MakeWindowControllers");
MainWindowController *mainWindowController = [[MainWindowController alloc] init];
[mainWindowController autorelease];
[self addWindowController:mainWindowController];
csvWindowController = [[CSVWindowController alloc] init];
[csvWindowController autorelease];
[self addWindowController:csvWindowController];
}
Problem is I want the second window controller csvWindowController to hide its window initially, I will show the same instance of the window later on. To do so I have written:
#implementation CSVWindowController
- (id) init {
if ( ! (self = [super initWithWindowNibName:#"CSVWindow"]) ) {
NSLog(#"CSVWindowController init failed");
return nil;
}
window = [self window];
NSLog(#"CSVWindowController init");
[window orderOut:nil]; // to hide it
NSLog(#"CSVWindowController hiding the window");
return self;
}
But the window is there, showing up.
Please not I have the VisibleAtLaunch not flagged, that console it's showing my messages correctly, and that even if I change:
[window orderOut:nil]; // to hide it
to
[window orderOut:self]; // to hide it
The result is the same, window showing up.
Any help is appreciated, thanks :)

Ok, again I reply to my own question, but this time with a positive remark. I think what I was doing wrong had something to do with the hidden - for me - implications of the Document-based architecture of the default Document Application template.
I have tried with a different approach, creating an application from scratch NOT flagging "Document-based Application" and providing it with:
1 NSDocument subclass
2 NSWindowControllers subclasses
1 MainMenu.xib
2 window.xib
and I have forced instantiation of the NSWindowController subclasses in the MyDocument code.
I have also put the IBActions for the MenuItems in the MyDocument and I have bound the MyDocument Object to the MenuItems in the MainMenu.xib.
This time I was able to do whatever, hiding/showing windows starting with one hidden one not, enabling menu items automatically at will.
Here follows the code, for any newbie like me who might have to fight with this in the future.
// MyDocument.h
#import <Cocoa/Cocoa.h>
#import "testWindowController.h"
#import "test2WindowController.h"
#interface MyDocument : NSDocument {
testWindowController *test;
test2WindowController *test2;
}
- (IBAction)showWindow1:(id)pId;
- (IBAction)showWindow2:(id)pId;
- (IBAction)hideWindow1:(id)pId;
- (IBAction)hideWindow2:(id)pId;
#end
// MyDocument.m
#import "MyDocument.h"
#import "testWindowController.h"
#import "test2WindowController.h"
#implementation MyDocument
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
NSLog(#"MyDocument init...");
[self makeWindowControllers];
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)makeWindowControllers
{
test = [[testWindowController alloc] init];
test2 = [[test2WindowController alloc] init];
[self addWindowController:test];
[self addWindowController:test2];
// start hiding the first window
[[test window] orderOut:self];
}
- (IBAction)hideWindow1:(id)pId
{
NSLog(#"hideWindow1");
[[test window] orderOut:self];
}
- (IBAction)showWindow1:(id)pId
{
NSLog(#"showWindow1");
[test showWindow:self];
[[test window] makeKeyAndOrderFront:nil]; // to show it
}
- (IBAction)hideWindow2:(id)pId
{
NSLog(#"hideWindow2");
[[test2 window] orderOut:self];
}
- (IBAction)showWindow2:(id)pId
{
NSLog(#"showWindow2");
[test2 showWindow:self];
[[test2 window] makeKeyAndOrderFront:nil]; // to show it
}
-(BOOL)validateMenuItem:(NSMenuItem *)menuItem {
NSLog(#"in validateMenuItem for item: %#", [menuItem title]);
if ([[menuItem title] isEqualToString:#"Show Window"]
&& [[test window] isVisible]){
return NO;
}
if ([[menuItem title] isEqualToString:#"Hide Window"]
&& ![[test window] isVisible]){
return NO;
}
if ([[menuItem title] isEqualToString:#"Show Window2"]
&& [[test2 window] isVisible]){
return NO;
}
if ([[menuItem title] isEqualToString:#"Hide Window2"]
&& ![[test2 window] isVisible]){
return NO;
}
return [super validateMenuItem:menuItem];
}

This is another method to prevent NSDocument's window(s) to show up early:
Subclass NSDocuments's window in IB
Use a flag to signal when window content is ready
Override makeKeyAndOrderFront method.
#interface DocWindow : NSWindow
#property BOOL inited;
#end
#implementation DocWindow
- (void)makeKeyAndOrderFront:(id)sender
{
if ( _inited )
[super makeKeyAndOrderFront:sender];
}
#end
#implementation Document
- (void)windowControllerDidLoadNib:(NSWindowController *)windowController
{
// prepare window content here.
...
// show doc's window when ready
DocWindow *win = (DocWindow *)self.window;
win.inited = YES;
[win makeKeyAndOrderFront:self];
}
#end

Related

How do I go about handling Cocoa Events in a fullscreen application

I have been trying to create an app for Cocoa without a nib/xib (No, I don't want to use a nib/xib. I want to be in full control programatically) and I can't seem to be able to catch the Events such as keystrokes and mouse clicks. Here is the code I have so far:
Main.m
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
NSApplication *app = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[app setDelegate:appDelegate];
[app activateIgnoringOtherApps:YES];
[app run];
}
return EXIT_SUCCESS;
}
AppDelegate.h/m
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
NSWindow *window;
}
#end
#import "AppDelegate.h"
#import "GLView.h"
#implementation AppDelegate
- (id)init{
self = [super init];
if (!self) {
return nil;
}
NSRect bounds = [[NSScreen mainScreen] frame];
GLView *view = [[GLView alloc]initWithFrame:bounds];
window = [[NSWindow alloc] initWithContentRect:bounds
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO];
[window setReleasedWhenClosed:YES];
[window setAcceptsMouseMovedEvents:YES];
[window setContentView:view];
return self;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[window makeKeyAndOrderFront:self];
}
#end
GLView.h/m
#import <Cocoa/Cocoa.h>
#interface GLView : NSView
#end
#import "GLView.h"
#implementation GLView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
}
- (BOOL)canBecomeKeyView
{
return YES;
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (BOOL)becomeFirstResponder
{
return YES;
}
- (BOOL)resignFirstResponder
{
return YES;
}
- (void)keyDown:(NSEvent *)theEvent
{
NSString* const character = [theEvent charactersIgnoringModifiers];
unichar const code = [character characterAtIndex:0];
NSLog(#"Key Down: %hu", code);
switch (code)
{
case 27:
{
EXIT_SUCCESS;
break;
}
}
}
- (void)keyUp:(NSEvent *)theEvent
{
}
#end
Nothing I have tried for it has worked. I thought that by setting the view as the first responder I would be able to get the events. So far... Not working. Any ideas on how I can fix this? Remember, NO NIB.
Thanks,
Tyler
First, you need to make sure that your window can actually become key, by subclassing and returning YES from canBecomeKeyWindow, because windows without title bars cannot become key by default.
Next, your build target needs to be an application. I would guess that you're starting from the Command-Line Tool template in Xcode. This is fine, but you need to produce an application bundle in order for your app to recieve key events. Create a new target in your project that will build a Cocoa Application. It needs to have an Info.plist file (from which you'll want to delete the "Main nib file base class" entry) and have a "Copy Bundle Resources" Build Phase.
I can't quite figure out what all the other differences are in the build process, but starting from your code, I got the window to accept key events with these two steps.

UIImagePIckerController appears but the camera does not start

Yes, It might be a duplicate question of this. But since it didn't get an answer, I will be more specific on the case and code:
I have 3 involved UIViewControllers:
WelcomeView - the first one
TakePhotoViewController - the second one who is delegate of the OverlayviewController
OverlayViewController - custom view for the camera.
Scenario:
User enter WelcomeView and clicks on a button to be transfered with segue to TakeView.
UIImageViewController is being opened to take a photo.
The user clicks on cancel button - didCancelCamera method in TakePhotoViewController is being invoked and he returns to WelcomeView
The user leaves the app.
The user re-opens the app and perform step 1 again.
THE IMAGE PICKER IS NOT BEING OPENED. I COULD TAKE A PHOTO AND IT'S OK - BUT THE USER CAN'T SEE WHAT HE IS TAKING.
OverlayViewController.h
#interface OverlayViewController : BaseViewController<UIImagePickerControllerDelegate,UINavigationControllerDelegate>
#property (nonatomic,weak) id<OverlayViewControllerDelegate> delegate;
#property (nonatomic,retain) UIImagePickerController *imagePickerController;
#end
OverlayViewController.m:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.imagePickerController = [[UIImagePickerController alloc] init];
self.imagePickerController.delegate = self;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor clearColor];
}
- (IBAction)takePicture:(id)sender {
[self.imagePickerController takePicture];
}
- (IBAction)cancelImagePicker:(id)sender {
[self.delegate didCancelCamera];
}
- (void) setupImagePicker:(UIImagePickerControllerSourceType) sourceType
{
self.imagePickerController.sourceType = sourceType;
if (sourceType == UIImagePickerControllerSourceTypeCamera)
{
self.imagePickerController.showsCameraControls = NO;
if ([[self.imagePickerController.cameraOverlayView subviews] count] ==0)
{
CGRect overlayViewFrame = self.imagePickerController.cameraOverlayView.frame;
CGRect newFrame = CGRectMake(0.0, CGRectGetHeight(overlayViewFrame)-self.view.frame.size.height-10.0, CGRectGetWidth(overlayViewFrame), self.view.frame.size.height + 10.0);
self.view.frame = newFrame;
[self.imagePickerController.cameraOverlayView addSubview:self.view];
}
}
}
- (void)finishAndUpdate
{
[NSThread detachNewThreadSelector:#selector(threadStartAnimating:) toTarget:self withObject:nil];
[self.delegate didFinishWithCamera]; // tell our delegate we are done with the camera
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[self finishAndUpdate];
}
TakePhotoViewController.h
#interface TakePhotoViewController : BaseViewController<UIImagePickerControllerDelegate,UINavigationControllerDelegate,OverlayViewControllerDelegate>
#property (nonatomic, retain) OverlayViewController *overlayViewController;
#end
TakePhotoViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
// Insert the overlay
self.overlayViewController = (OverlayViewController *)[sb instantiateViewControllerWithIdentifier:#"Overlay"];
self.overlayViewController.delegate = self;
}
- (void)viewDidUnload
{
self.overlayViewController = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (void)openImagePicker {
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){
[self showImagePicker:UIImagePickerControllerSourceTypeCamera];
}
else{
[self showImagePicker:UIImagePickerControllerSourceTypePhotoLibrary];
}
}
- (void)viewDidAppear:(BOOL)animated{
if (appDelegate.shouldOpenPicker){
[self openImagePicker];
}
}
- (void)showImagePicker:(UIImagePickerControllerSourceType)sourceType
{
if ([UIImagePickerController isSourceTypeAvailable:sourceType])
{
[self.overlayViewController setupImagePicker:sourceType];
[self presentViewController:self.overlayViewController.imagePickerController animated:YES completion:nil];
}
}
-(void)didCancelCamera{
[[self.overlayViewController.imagePickerController presentingViewController] dismissViewControllerAnimated:NO completion:^ {
[self performSegueWithIdentifier:#"fromTakeToWelcome" sender:self];
}];
}
I found the bug.
The method
-(void)didCancelCamera from TakePhotoViewController is being called when the user clicks on - (IBAction)cancelImagePicker:(id)sender in OverlayViewController.
However, somehow the code in didCancelCamera causes viewDidAppear method of TakePhotoViewController to be invoked again and reopen the image picker.
I have no idea why
[[self.overlayViewController.imagePickerController presentingViewController] dismissViewControllerAnimated:NO completion:^ {
[self performSegueWithIdentifier:#"fromTakeToWelcome" sender:self];
}];
causes the viewDidAppear method of that view (TakePhoto) being recalled again.
Hope that it will help someone

Receiving Drag events on an NSWindow when the window contains a single WebView view

I am new to objective-c and cocoa so please break things down for me. I'm working on a project started by another developer and have only been working in objective-c for 3 days.
I have an NSWindow subclass that contains a WebView view. The WebView content that is loaded is a Silverlight plugin. I registered the NSWindow to receive Drag events. The drag events are being generated but only when the drag occurs within the NSWindow Title Bar. I register for the drag events in the load method.
AdminWindow.mm
#import "AdminWindow.h"
#import "NativeMessageReceiver.h"
extern AdminWindow* adminRiaWindow;
#implementation AdminWindow
#synthesize adminWebView;
BOOL isAdminContentLoaded;
-(void) load
{
if (!isAdminContentLoaded)
{
NSLog(#"loading Admin window");
NSString *curDir = [[NSBundle mainBundle] bundlePath];
NSString* url = [NSString stringWithFormat: #"file://%#/Contents/Resources/RIA/AdminContentMac.html",curDir];
[[adminWebView mainFrame] loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString: url]]];
[adminWebView setDrawsBackground:NO];
id win = [adminWebView windowScriptObject];
NativeMessageReceiver* receiver = [NativeMessageReceiver getInstance];
[win setValue:receiver forKey:#"NativeMessageReceiver"];
receiver.adminWebView = adminWebView;
isAdminContentLoaded = YES;
}
}
-(void) show
{
[self load];
[self setIsVisible: YES];
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
[self makeKeyAndOrderFront: self];
[self makeMainWindow];
[self center];
}
-(void) hide
{
[self setIsVisible: NO];
}
- ( BOOL ) windowShouldClose : ( id ) sender
{
[self setIsVisible: NO];
return NO;
}
- (BOOL) canBecomeKeyWindow
{
return YES;
}
- (BOOL) canBecomeMainWindow
{
return YES;
}
#end
extern "C" void ShowAdminWindow()
{
NSLog(#"showing Admin window");
if (![NSThread isMainThread])
[adminRiaWindow performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
else
{
[adminRiaWindow show];
}
}
extern "C" void HideAdminWindow()
{
if (![NSThread isMainThread])
{
[adminRiaWindow performSelectorOnMainThread:#selector(hide) withObject:nil waitUntilDone:YES];
}
else
{
[adminRiaWindow hide];
}
}
CustomeWebView
#implementation SteamPunkWebView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
NSLog(#"In Custom View Init");
}
return self;
}
I think you're only getting drag events in the title bar because you registered the window to receive the events, not the web view.
Try this instead:
[self.adminWebView registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
WebViews are one of the most complex views in Cocoa and are a special case when it comes to dragging. Instead of implementing the standard NSView dragging behaviour, you need instead to set an object as the web view's WebUIDelegate and implement the following delegate method:
‑webView:dragDestinationActionMaskForDraggingInfo:
You can then control how you want the web view to respond to the drag action. The WebUIDelegate protocol has several other methods to control dragging, so you should read the documentation for more detail.
To actually receive the dragged objects and process them, you will need to subclass WebView and implement ‑draggingEnded:.
You definitely do not need to add any dragging methods to the window itself.

Prevent window from opening twice…

I execute these lines to show the preferences window:
-(IBAction)showPreferences:(id)sender {
PreferencesWindowController *preferencesWindowController = [[PreferencesWindowController alloc] init];
NSNib *preferencesNib = [[NSNib alloc] initWithNibNamed:#"PreferencesWindow" bundle:nil];
[preferencesNib instantiateNibWithOwner:preferencesWindowController topLevelObjects:nil];
[NSApp activateIgnoringOtherApps:YES];
[[preferencesWindowController window] makeKeyAndOrderFront:nil];
[preferencesNib release];
}
But when the user clicks a second time on the preferences button (and the preferences window is still open) it will open up another instance of the preferences window.
How should I prevent this without hacking around with control variables? Should I edit my PreferencesWindowController to be a singleton?
My approach would be to make a PreferencesWindowController ivar in whatever class this action belongs to:
#interface foo : NSObject
{
#private
PreferencesWindowController *_pwc;
}
- (IBAction) showPreferencesWindow:(id)sender;
#end
#implementation foo
- (void) dealloc
{
[_pwc release], _pwc = nil;
[super dealloc];
}
- (IBAction) showPreferencesWindow:(id)sender
{
if(nil == _pwc)
_pwc = [[PreferencesWindowController alloc] initWithWindowNibName:#"PreferencesWindow"];
[_pwc showWindow:sender];
}
#end

iPhone delegate & controller dealloc?

I have been playing with a simple iphone app and decided to put an NSLog statement in the deallocs of both the controller and the delegate but neither of these print out to the Xcode console?
// APPLICATION DELEGATE
#import "iPhone_buttonFunAppDelegate.h"
#import "iPhone_buttonFunViewController.h"
#implementation iPhone_buttonFunAppDelegate
#synthesize window;
#synthesize viewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after app launch
NSLog(#"applicationDidFinishLaunching ...");
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
- (void)applicationWillTerminate:(UIApplication *)application {
NSLog(#"AWT:");
}
- (void)dealloc {
NSLog(#"-DEL-"); // << Does not print?
[viewController release];
[window release];
[super dealloc];
}
#end
// VIEW CONTROLLER
#import "iPhone_buttonFunViewController.h"
#implementation iPhone_buttonFunViewController
#synthesize statusText;
-(IBAction)buttonPressed:(id) sender {
NSString *title;
NSString *newText;
title = [sender titleForState:UIControlStateNormal];
newText = [[NSString alloc] initWithFormat:#"%# button pressed.", title];
[statusText setText:newText];
[newText release];
NSLog(#"Button Press ... %#", title);
}
-(void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
NSLog(#"-1-");
}
-(void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
NSLog(#"-2-");
self.statusText = nil;
}
-(void)dealloc {
NSLog(#"-CON-"); // << Does not print?
[statusText release];
[super dealloc];
}
#end
gary
This is an optimization in the Cocoa touch runtime. Certain deallocations aren't made at the end of the program, since the entire program is going to exit and they will be wiped out anyway.
This problem with NSLog(...) may be answered by this other stackoverflow question about applicationWillTerminate:
Good luck.