I am using this code:
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel beginForDirectory:nil file:nil types:[NSImage imageFileTypes] modelessDelegate:self didEndSelector:NULL contextInfo:NULL];
This is the only code in the method. When the method is called, the open panel appears on-screen for a second then disappears. How do I prevent this?
Thanks.
Since the panel is non-blocking, code execution continues once the panel has opened. The open panel is being deallocated because you are not holding a reference to it somewhere. -openPanel is a convenience constructor and returns an autoreleased object which will go away when the current autorelease pool is popped or (in a GC app) when the collector is next run. In your case, this is as soon as your method has finished.
If you want the panel to stick around, you must specifically retain it using -retain, and then subsequently -release it in the didEndSelector:
- (void)showPanel
{
NSOpenPanel *openPanel = [[NSOpenPanel openPanel] retain]; //note the retain
[openPanel beginForDirectory:nil
file:nil
types:[NSImage imageFileTypes]
modelessDelegate:self
didEndSelector:#selector(myOpenPanelDidEnd:returnCode:contextInfo:)
contextInfo:NULL];
}
- (void)myOpenPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void*)contextInfo
{
NSArray* fileNames = [panel filenames];
[panel release];
//do something with fileNames
}
If you're using Garbage Collection, retain and release are no-ops, so you must instead store a strong reference to the NSOpenPanel, such as storing it in an instance variable.
Related
I received a warning from XCode during the execution of my program :
2016-01-21 03:19:26.468 IsoMetadonnees[1975:303] An instance 0x1004eefd0 of class NSVBOpenPanel was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x608000444710> (
<NSKeyValueObservance 0x6080000d5310: Observer: 0x100592cf0, Key path: level, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x6080004486a0>
)
The problem occurs while the application presents an NSOpenPanel to select some files that will be asynchronously loaded. The application does not crash and file are correctly loaded...
I don't create any value observer, so I imagine that the observer is created by NSOpenPanel, but I don't know any procedure to remove observer that I have not created...
Despite of this warning, I have made multiple loads without notice any crash. I use my application since many years without any problems, but I recently switch to ARC; may be the problem appeared (or is detected) at this time.
Here is a simplified version of my code :
- (IBAction)ajoutFichier:(id)sender {
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
// Here some configurations of openPanel
if ([openPanel runModal] == NSOKButton) {
tmp_listeURLFichiers = [openPanel URLs];
}
//[openPanel close]; // I add this code unsuccessfully
openPanel = nil; // I add this code unsuccessfully
// I call a task in back ground to load my files
if ((tmp_listeURLFichiers != nil) && ([tmp_listeURLFichiers count]>0))
[self performSelectorInBackground:#selector(ajouteListeFichiers:) withObject:tmp_listeURLFichiers];
}
// Load files in background
-(BOOL) ajouteListeFichiers:(NSArray *)listeDesFichierAAjouter {
#autoreleasepool {
// Some stuff to show a progress bar
// Loop to load selected files
for (id tmpCheminVersMonImage in listeDesFichierAAjouter) {
// Load files
}
} // <========== THE WARNING OCCURS AT THIS POINT, WHEN autoreleasepool is cleaned
return (YES);
}
I try adding
[openPanel close];
and
openPanel = nil;
to force releasing openPanel from memory (and thus observers) before starting background task, but that doesn't change anything...
Do you have any idea ?
Thank you for your help !
I could fix the problem using the following trick :
I declare a variable in my view controller :
__strong NSOpenPanel *prgOpenPanel;
Then I use it in my code
//NSOpenPanel *prgOpenPanel = [NSOpenPanel openPanel];
self.prgOpenPanel = nil;
self.prgOpenPanel = [NSOpenPanel openPanel];
// Here some configurations of openPanel
if ([prgOpenPanel runModal] == NSOKButton) {
tmp_listeURLFichiers = [prgOpenPanel URLs];
if ((tmp_listeURLFichiers != nil) && ([tmp_listeURLFichiers count]>0))
[self performSelectorInBackground:#selector(ajouteListeFichiers:) withObject:tmp_listeURLFichiers];
}
No more warnings !
It's been a year since I last played with Cocoa and it seems a lot has changed.
I am trying to run an open dialog and retrieve the file path. This used to be very simple but now...
The code is:
-(NSString *)getFileName{
NSOpenPanel* panel = [NSOpenPanel openPanel];
__block NSString *returnedFileName;
// This method displays the panel and returns immediately.
// The completion handler is called when the user selects an
// item or cancels the panel.
[panel beginWithCompletionHandler:^(NSInteger result){
if (result == NSFileHandlingPanelOKButton) {
NSURL* theDoc = [[panel URLs] objectAtIndex:0];
// Open the document.
returnedFileName = [theDoc absoluteString];
}
}];
return returnedFileName;
}
-(IBAction)openAFile:(id)sender{
NSLog(#"openFile Pressed");
NSString* fileName = [self getFileName];
NSLog(#"The file is: %#", fileName);
}
(The indentation has been screwed up in the post but it's correct in the code)
My problem is that the final NSLog statement is being executed as soon as the open dialog opens and not waiting until the dialog closes. That leaves the fileName variable null which is what the final NSLog reports.
What is causing this?
Thanks.
There is a similar question to yours:
How do I make my program wait for NSOpenPanel to close?
Maybe
[openPanel runModal]
helps you. It waits until the user closes the panel
The stuff I had written a year ago used runModal so on Christoph's advice I went back to that.
It would appear that the beginWithCompletionHandler block is unnecessary, at least in this case. Removing it also had the advantage of removing the necessity to use the __block identifier.
The following now works as required
-(NSString *)getFileName{
NSOpenPanel* panel = [NSOpenPanel openPanel];
NSString *returnedFileName;
// This method displays the panel and returns immediately.
// The completion handler is called when the user selects an
// item or cancels the panel.
if ([panel runModal] == NSModalResponseOK) {
NSURL* theDoc = [[panel URLs] objectAtIndex:0];
// Open the document.
returnedFileName = [theDoc absoluteString];
}
return returnedFileName;
}
And well done Apple for deprecating the obvious and easy and replacing it with increased complexity.
This is a pretty weird issue. I have a table in my Cocoa application that displays a list of recently opened files. You can double click on an entry to open the associated file. Trouble is, though, after the file is opened once, whether through the Open panel, the Recent Documents menu, or through the aforementioned table, it can't be opened again until the application has quit and re-opened. Other documents, however, can be opened, but once they're closed they can't be opened again either.
This is pretty odd behavior, and I'm not sure what's causing it. But it's certainly annoying. For reference, the Release on Closed attribute of the window from Xcode does nothing and, if selected, does not do anything. I can't think of any other attributes which might cause this behavior. For reference, here's a photo of the attributes panel:
Here's the code for the table which opens the recently opened file:
- (void)respondToRecentFileDoubleClick {
NSInteger clickedRow = [_recentFileBrowser clickedRow];
if (clickedRow != -1) { // We're in the row.
NSDocumentController *docControl = [NSDocumentController sharedDocumentController];
NSURL *selectedDocument = (NSURL *)[docControl recentDocumentURLs][clickedRow];
NSLog(#"Selected row %ld.", (long)clickedRow);
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:selectedDocument display:YES completionHandler:nil];
}
}
The documentation for openDocumentWithContentsOfURL: says that the document won't be opened if it's already opened, but in this case, all of the document windows are closed, so that can't be what causes this behavior. And the NSLog() statement inside the if block prints, so I know the code is being executed.
Anyone know what might be causing this bizarre issue?
From the Xcode image, it appears that you are using your own WindowController. The default close menu item is wired up to call performClose. The performClose method is implemented in NSWindow. So, what is happening is that the window is closing, but the document is not removed from the open document list. Try adding < NSWindowDelegate> to your WindowController interface (in the .h file). Then add to your WindowController .m file:
- (void) windowWillClose:(NSNotification *)notification {
[ourdoc close];
}
Substitute whatever variable you using to hold your document reference for ourdoc. Typically the method setDocument will get called with your document reference. (Also in your WindowController.)
- (void) setDocument:(NSDocument *)document {
ourdoc = (yourNSDocumentsubclass *)document;
}
completionHandler:nil change for debugging:
completionHandler:^(NSDocument *doc, BOOL documentWasAlreadyOpened, NSError *error) {
if (documentWasAlreadyOpened) {
NSLog(#"document was already opened");
NSArray *rats = [[NSDocumentController sharedDocumentController] documents];
NSLog(#"%s seriously: %#", __PRETTY_FUNCTION__, rats);
}
}
So I currently have this bit of code to get a dir:
-(NSString *)get {
NSOpenPanel *gitDir = [NSOpenPanel openPanel];
NSInteger *ger = [gitDir runModalForTypes:nil];
NSString *Directory = [gitDir directory];
return Directory;
}
But it gives me errors and says it has now been depreciated.
Is there a better way for OSX 10.7?
This is a supplement to sosborn's answer, not a replacement.
runModalForTypes: is deprecated, and the correct replacement is runModal (or setAllowedFileTypes: followed by runModal, but in this case you're passing nil for the types).
directory is also deprecated, and the correct replacement is directoryURL. (If you actually must return an NSString path rather than an NSURL, just return [[gitDir directoryURL] path].)
However, what you're doing is asking the user to select a file, and then returning the directory that file is in, when what you really want is to ask the user to select a directory. To do that, you want to call setCanChooseFiles to NO and setCanChooseDirectories to YES, and then call URLs to get the directory the user selected.
Also, you're ignoring the result of runModal (or runModalForTypes:). I'm sure the compiler is warning you about the unused variable "ger", and you shouldn't just ignore warnings. If the user cancels the panel, you're going to treat that as clicking OK, and select whatever directory she happened to be in when she canceled.
Here's a better implementation, which will return the URL of the selected directory, or nil if the user canceled (or somehow managed to not select anything). Again, if you need an NSString, just add a "path" call to the return statement:
-(NSURL *)get {
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setAllowsMultipleSelection:NO];
[panel setCanChooseDirectories:YES];
[panel setCanChooseFiles:NO];
if ([panel runModal] != NSModalResponseOK) return nil;
return [[panel URLs] lastObject];
}
Whenever you see a deprecation warning you should go straight to the official documentation. In this case, the docs for NSOpenPanel say:
runModalForTypes: Displays the panel and begins a modal event loop
that is terminated when the user clicks either OK or Cancel.
(Deprecated in Mac OS X v10.6. Use runModal instead. You can set
fileTypes using setAllowedFileTypes:.)
I adapted the code by abarnert for swift. tx for the code just what I needed.
func askUserForDirectory() -> NSURL? {
let myPanel:NSOpenPanel = NSOpenPanel()
myPanel.allowsMultipleSelection = false
myPanel.canChooseDirectories = true
myPanel.canChooseFiles = false
if ( myPanel.runModal() != NSFileHandlingPanelOKButton ) {
return nil
}
return myPanel.URLs[0] as? NSURL
}
I have a program which works normally. Then I downloaded some code from http://github.com/matej/MBProgressHUD to show a progress meter when doing something.
This is the code that makes the progress meter pop up.
[HUD showWhileExecuting:#selector(myTask) onTarget:self withObject:nil animated:YES];
This will show a progress meter while the method myTask is running.
This is the code for the showWhileExecuting method.
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated {
methodForExecution = method;
targetForExecution = [target retain];
objectForExecution = [object retain];
// Launch execution in new thread
taskInProgress = YES;
[NSThread detachNewThreadSelector:#selector(launchExecution) toTarget:self withObject:nil];
// Show HUD view
[self show:animated];
}
If I use this to call the function myTask then one of my class properties will change from an NSMutableString to an NSData object somewhere, and then later on it will change to an NSString. I don't see anywhere in the code that causes this to change, so it's probably some kind of bug. Is memory getting corrupted? What's causing this to happen?
Most likely it's a memory (retain/release issue). If you don't properly retain an object, it may get released out from under you. At that point, the memory will be reclaimed by the OS, which may decide to store something else there. Try turning on NSZombies, and double checking your retain/release/autoreleases.