NSWindow created in IB won't show modally in the application - objective-c

I have an application that when pressing a button it should show a modal window. I have some questions about it but I'm going to narrow it to what it's giving me a headache.
I want to add that I'm already doing something alike that when applied to this particularly case won't work. What I already do is to show modally a NSOpenPanel. This is the code for it.
This code is inside the appDelegate.
-(IBAction) respondToPublishAction:(id) sender {
NSArray *fileTypes = [NSArray arrayWithObjects:#"xml", #"txt", nil];
NSOpenPanel *oPanel = [NSOpenPanel openPanel];
NSString *startingDir = [[NSUserDefaults standardUserDefaults] objectForKey:#"StartingDirectory"];
if (!startingDir)
startingDir = NSHomeDirectory();
//Setting the attributes
[oPanel setAllowsMultipleSelection:NO];
[oPanel setTitle:#"XML file for publishing services"];
[oPanel beginSheetForDirectory:startingDir file:nil types:fileTypes
modalForWindow:[self window] modalDelegate:self
didEndSelector:#selector(openPanelDidEnd:returnCode:contextInfo:)
contextInfo:nil];
}
Now What I'm trying to do and that won't work
I created a xib file that has a HUD Window (how it's named in IB) that acts like a NSPanel. I created a windowcotroller that listen to a button (testButton). Here's the code:
#interface BrowserWindowController : NSWindowController {
}
-(IBAction) testButton:(id) sender;
#end
I set the file's owner of the xib file to be of the class BrowserWindowController and linked the button with the testbutton.
Back to the appDelegate when a button is clicked I want to show this Panel modally. Here's the code:
- (IBAction) respondToBrowseAction:(id) sender {
browsePanel = [[BrowserWindowController alloc] initWithWindowNibName:#"BroserWindow"];
}
I don't see any function in the API like NSOpenPanel to show this window modally.
Thx to anyone who might answer.

The reason there is no NSWindow API for running modally is because it's the application that is going into a modal mode. See How Modal Windows Work and Introduction to Sheets.

Related

NSPopover deallocation when losing focus (OSX, Objective-C)

I am on OSX (not iOS), Xcode 8.2, ARC enabled, Objective-C.
I have a view that opens a popover on a button click. Both are connected with a delegate and a protocol that allows access to the following methods (among others)
- (id)valueForKey:(NSString*)key;
- (void)setValue:(id)value forKey:(NSString *)key;
(I'm using this protocol very often and need to keep it as clean and unspecific as possible)
On the popover's view there's another button that opens an NSOpenPanel. Once the panel opens, the popover dissapears and get's deallocated - which is the preferred behaviour... usually. Unfortunately i need the openPanel to save to the parent view via the protocol methods. But if the popover gets deallocated those methods are not available anymore.
So i tried to create a strong reference to the value i want to save to before the openPanel and release it afterwards to keep the popover viewController in memory. But once i try to release that... it crashes.
#interface SHWildcardItemSettingImageViewController ()
#property (strong) NSString* customImagePathBookmark;
#end
#implementation SHWildcardItemSettingImageViewController
// Create strong reference to delegate (to not dealloc popoverViewController)
[self setCustomImagePathBookmark:[_delegate valueForKey:#"customImagePathBookmark"]];
// Setup open panel
NSOpenPanel *openPanel = [[NSOpenPanel alloc] init];
[openPanel setAllowsMultipleSelection:NO];
[openPanel setCanChooseDirectories:YES];
[openPanel setCanChooseFiles:NO];
[openPanel setCanCreateDirectories:YES];
[openPanel setPrompt:#"Choose folder"];
// Display the dialog box. If the OK pressed, process the folder.
if ( [openPanel runModal] == NSModalResponseOK ) {
// Gets list of all files selected
NSArray *files = [openPanel URLs];
// Create and save Bookmark for later use (_bookmarks is a custom NSObject)
[_delegate setValue:[_bookmarks bookmarkFromURL:[files objectAtIndex:0]] forKey:#"customImagePathBookmark"];
}
[self setCustomImagePathBookmark:nil]; // <---- Crashes here
#end
What would be the best way to keep the popover allocated while the NSOpenPanel is in focus WITHOUT using different NSPopoverBehaviours.
EDIT:
Sometimes you don't see it... thanks to #Ron Gahlot
- Solved with a simple bool check:
- (BOOL)popoverShouldClose:(NSPopover *)popover
{
// Check if imageSettings can close
if ([popover.contentViewController isMemberOfClass:[SHWildcardItemSettingImageViewController class]])
return ![[popover.contentViewController valueForKey:#"lockPopover"] boolValue];
return YES;
}
you use a bool variable. when openDialog is open return NO in
- (BOOL)popoverShouldClose:(NSPopover *)popover;

Sheet Modal alert triggered from other class than ViewController Cocoa Mac Storyboard Xcode 6

I have a simple project with a storyboard for a Mac OS X app. I placed a button on the NSView and created a new class called OtherClass. Then I linked an IBAction to the OtherClass.h. Now I want to make appear a model sheet in the main window. When using ViewController as class I could simply do:
NSWindow *window = [self.view window];
NSAlert *alert = [[NSAlert alloc]init];
[alert beginSheetModalForWindow:window completionHandler:nil];
But how do I do this from another class? I am using Xcode 6.3 if this is important.
Thanks for your help
You can simply refer the window object associated to button.
So you can call like this from your OtherClass
-(IBAction)callSheet:(id)sender {
NSWindow *window;
if([sender respondsToSelector(#selector(window))]){
window = [sender window];
}else if([sender respondsToSelector(#selector(view))]){
window = [sender view].window;
}
//Now call your alert
}

unlockFocus called too many times

I click on a NSButton (openPanel) which is located on the main window of my OS X app to show a NSPanel (myPanel) using the following method:
- (IBAction)openPanel:(id)sender {
[_myPanel makeKeyAndOrderFront:nil];
}
Everything works as expected except I get the following description in the debug area only the first time I click on the button (openPanel):
"unlockFocus called too many times. Called on NSButton: 0x610000141080."
The attributes of the Panel are selected as follows:
Style: Utility Panel,
Appearance:Title Bar and Shadow,
Controls: Close,
Behaviour: Restorable,
Memory: Deferred
I have looked through the web but can not find any explanation. Does anyone know why this happens or how to resolve it?
I had the same problem using XCode 7.0 beta 6 using storyboards and a segue to display a sheet. It seems some common problem which happens if you open the panel directly from the action.
To solve the problem, just open the panel from the main queue:
- (IBAction)openPanel:(id)sender {
dispatch_async(dispatch_get_main_queue(), ^{
[_myPanel makeKeyAndOrderFront:nil];
});
}
This will also solve similar problems from swift and storyboards. Use this swift code to call a segue:
#IBAction func onButtonClicked(sender: AnyObject) {
dispatch_async(dispatch_get_main_queue(), {
self.performSegueWithIdentifier("identifier", sender: self);
})
}
Not sure what is going on there but I would try this to show your NSPanel:
-(IBAction)openPanel:(id)sender {
[[NSApplication sharedApplication] beginSheet:_myPanel
modalForWindow:self.window
modalDelegate:self
didEndSelector:#selector(sheetDidEnd:returnCode:contextInfo:)
contextInfo:nil];
Also you should make sure that Release When Closed checkbox is activated for your NSPanel.
Search for it in the Interface Builder.
EDIT: Are you drawing something on your NSPanel ? Setting a NSColor or showing a NSImage ?
My guess is that you are displaying a NSImage and messed something up concerning this: Apple Doc: lockFocus
I have fixed the same problem with the following
In Header
#interface aWindowController : NSWindowController
{
/* other Vars */
NSOpenPanel *panel;
}
In .m
- (void)windowDidLoad {
[super windowDidLoad];
/* other stuff */
/* set Panel Properties */
panel = [NSOpenPanel openPanel];
[panel setCanChooseDirectories:YES];
[panel setCanChooseFiles:NO];
[panel setAllowsMultipleSelection:YES];
}
in NSButton IBACTION
- (IBAction)getFiles:(id)sender
{
NSArray *resultArry;
if([panel runModal] == NSFileHandlingPanelOKButton ){
resultArry = [panel URLs];
return resultArry;
}
return nil;
}
ARC will clean up the panel.
I had the same problem with a NSPopUpButton that called a Segue and #Flovdis answer worked for me.
This is the Objective C code I used:
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"MySegue" sender:self];
});

Objective-C: How to switch the NSView by button click

I have different xib files with NSViewController attached to them. (Screenshot below)
One of xib file called StartMenuViewController which has a button. I want to click that button and change the view to DetectingUSBViewController.(Screenshot below)
The IBAction of that button is in StartMenuViewController.m file.
And I use AppController.m to control my main xib view.(NSWindow + NSView) (Screenshot below)
When the application runs, I try to initialize the StartMenuViewController fist by doing the following thing in my AppController.m file.
-(void)awakeFromNib{
[self initialize];
}
-(void) initialize
{
#autoreleasepool {
//mainViewController is a NSViewController and _mainView is a NSView which connect with Custom View in main xib
self.mainViewController = [[[StartMenuViewController alloc]initWithNibName:StartMenuView bundle:nil]autorelease];
[_mainView addSubview:[_mainViewController view]];
}
}
It works fine and it will show the StartMenuViewController.xib on the window at first, but I do not know how to change the view after clicking the button(FIND USB DRIVE). I want the current view changes to DetectingUSBViewController.xib.
Simplest way possible, assuming you have tied your USB button properly in, do the following :
- (IBAction)usbButton:(UIButton *)sender {
DetectingUSBViewController *second = [[DetectingUSBViewController alloc] initWithNibName:#"DetectingUSBView" bundle:nil];
[self presentViewController:second animated:YES completion:nil];
}
load the DetectingUSBViewController in startMenuViewController as DetectingUSBViewController* v1 = [[ViewCont1 alloc] initWithNibName:#"ViewCont1" bundle:nil]; now add or replace the view as [v1 view] in view where you want to add/replace.
You need to hook up your button to send an IBAction
You need a 'View for DetectingUSBViewController.xib'
=> one way (iOS like) is to use a ViewController. Subclass NSViewController and then alloc init a DetectingUSBViewController
Add the view. Don't present the VC (as there is no such thing in OSX)
//button click action
- (IBAction)usbButton:(UIButton *)sender {
//! Retain the VC
Self.detectingUSBViewController = [[DetectingUSBViewController alloc] initWithNibName:#"DetectingUSBView" bundle:nil];
//add the view
[_mainView addSubview:[_detectingUSBViewController view]];
}

Best design pattern for a window opening another window in cocoa application

I am learning how to create osx applications with Cocoa/Objective-C. I am writing a simple app which will link together two different tutorials I have been going through. On start up a choice window loads with 2 buttons, one button loads one window and the other loads the other window. When either button is clicked the choice window closes.
The choice window controller object was added to the MainMenu.xib file so it is created at launch. The window is then opened using the awakeFromNib message.
I want the result of one button to open up the 'track controller' tutorial application from the ADC website. The action looks like this:
- (IBAction)trackButton:(id)sender {
TMTrackController *trackController = [[TMTrackController alloc] init];
[self.window close];
}
I added an init method to the TMTrackController class which looks like this:
- (id) init {
if (self = [super init]) {
[self showWindow];
TMTrack *myTrack = [[TMTrack alloc] init];
myTrack.volume = 50;
self.track = myTrack;
[self updateUserInterface];
return self;
}
else {
return nil;
}
}
- (void) showWindow {
if(!self.window) {
[NSBundle loadNibNamed:#"trackWindow" owner:self];
}
[self.window makeKeyAndOrderFront:self];
}
I am not sure this is the best way to be doing this as I know that the choiceController class will be released when it is closed thus getting rid of the TMTrackController class too. However even when I untick the 'release when closed' box of the ChoiceWindow.xib it breaks too.
What is the correct way to do this?
With xib s in the same project use:
#interface
#property (strong) NSWindowController *test;
#implementation
#synthesize test;
test = [[NSWindowController alloc] initWithWindowNibName:#"XIB NAME HERE"];
[test showWindow:self];
[home close];
It is not completely the same but this is my solution for such problems: Stackoverflow
Just ignore my statement in this answer regarding showing the window as a modal window. Everything else is still valid. This way you could have your personal window controller and it controls everything there is within the xib. This is a huge advantage for maintaining the project afterwards (and you keep to the application logic).