Objective-C Directory Picker ( OSX 10.7 ) - objective-c

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
}

Related

NSOpenPanel and detected leaked observer

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 !

Getting filename from NSOpenPanel dialog

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.

NSSavePanel not saving on desktop?

can i use NSSavePanel with a sandboxed OS X app to let user save on desktop? i gave user read/write entitlements for downloads and user selected folder, for some reason my app saves in downloads folder fine but when i change directory and select desktop it doesnt save at all.
here is the code am using for NSSavePanel
if([self.mActiveQRFileName isEqualToString:kQR_DEFAULT_FILE_NAME])
{
NSSavePanel *savePanel = [NSSavePanel savePanel];
//[savePanel setDirectoryURL:[NSURL URLWithString:[Utilities getQRDefaultDirectoryPath]]];
[savePanel setNameFieldStringValue:kQR_DEFAULT_FILE_NAME];
[savePanel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) {
if (result == NSFileHandlingPanelOKButton) {
NSString *qrFilePath = [NSString stringWithFormat:#"%#.%#",[[savePanel URL] path],kQR_FILE_EXT];
[qrd saveQRFile:qrFilePath];
self.mActiveQRFileName = [NSString stringWithString:qrFilePath];
blnChangesSaved = YES;
}
}];
}
else
{
[qrd saveQRFile:self.mActiveQRFileName];
blnChangesSaved = YES;
}
Any help would be greatly appreciated :)
A NSSavePanel will give you the user selected path for a file in it's URL property. The sandbox will only grant you access to this file, with the name specified by the user.
In your example code this line possibly modifies the selected path by giving it a different file extension:
NSString *qrFilePath = [NSString stringWithFormat:#"%#.%#",[[savePanel URL] path],kQR_FILE_EXT];
Which could result in a filename different from the originally selected file for which you don't have access in the sandbox. Try logging the qrFilePath and see if it still equals the path for the selected URL. Also check your sandbox exceptions to see what the exact error is.
If you want to restrict the NSSavePanel to let the user only specify files of a certain type use the setAllowedFileTypes: methos.
If you want the user to grant you access to a directory to write to where you can output any file, as opposed to a specific path: use a NSOpenPanel. This has the disadvantage that the user cannot specify a specific file name like in a NSSavePanel.

NSOpenPanel allowed to select packages but not show contents

I am trying to get an NSOpenPanel to do the following:
Cannot select files
Can select directories and packages
Cannot see package contents
In order to get the first 2 points I need to use:
[openDlg setCanChooseFiles:NO];
[openDlg setCanChooseDirectories:YES];
[openDlg setTreatsFilePackagesAsDirectories:YES];
However this means that when in column view and a package is selected, the contents of the package are shown. I want the behaviour which occurs when we have [openDlg setCanChooseFiles:YES]; [openDlg setTreatsFilePackagesAsDirectories:NO]; i.e. the package can be selected but the column view browser does not show the contents when it is selected.
Any ideas?
There's a now deprecated method in the NSSavePanel's delegate with a method name of:
- (BOOL) panel: (id) sender shouldShowFilename: (NSString *) filename]
which can be used to tell the save panel to not display certain filenames.
Details about how to use it can be seen in this Apple QA technote, which details how to do exactly the opposite of what you are trying to do (their example is how to choose any file but ignore packages, but you may be able to flip the internal logic around).
Now, remember that I said that the method is "deprecated". The NSSavePanel header file says this:
/* This method is deprecated in 10.6, and will be formally deprecated */
/* in a future release. Use panel:shouldEnableURL: instead */
- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename;
What NSOpenSavePanelDelegate's panel:shouldEnableURL: apparently does it merely allow or disallow the file from being selectable.
To future-proof your app, you may need to do the respondsToSelector trick to make sure "shouldShowFilename" is still available as an option before using the less desirable "shouldEnableURL" method.
I implemented the NSOpenPanelDelegate method panel:shouldEnableURL: as follows:
- (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url {
BOOL showObject = NO;
// This checks if the path is a directory
[[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory:&showObject];
// This checks if the path is a package
if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:[url path]]) {
showObject = YES;
}
return showObject;
}
This doesn't require any further configuration (like setCanChooseDirectories:) and does exactly what I want!

NSOpenPanel's setDirectoryURL doesn't work

I'm trying to use the new methods for NSOpenPanel and set its initial directory. The problem is that it only works at the first time and after that it just "remembers" the last selected folder, which I don't want. I have to use the depreciated runModalForDirectory:file: to make it work. It's less than ideal because it was deprecated at 10.6, but thankfully it still works on Lion.
My code is:
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setAllowedFileTypes:[NSArray arrayWithObjects: #"jpg",#"JPG",#"png", nil]];
panel.canChooseDirectories = YES;
panel.allowsMultipleSelection = YES;
handler = ^(NSInteger result) {stuff};
[panel setDirectoryURL:[NSURL URLWithString:#"/Library/Desktop Pictures"]];
There are a couple things to look into:
~/Pictures is not a valid URL. file:///Users/user/Pictures is. -[NSURL URLWithString:] requires a valid URL. You probably want to use -[NSURL fileURLWithPath:] instead. It will turn /Users/user/Pictures into file:///Users/user/Pictures.
Tildes are not automatically expanded, so you want to use [#"~/Pictures stringByExpandingTildeInPath] to get an actual file path.
Put together, change the last line to:
[panel setDirectoryURL:[NSURL fileURLWithPath:[#"~/Pictures" stringByExpandingTildeInPath]]];
I think that should work.
The panel in Lion expects an URL like: file://localhost/Library/Desktop Pictures, but your URL starts with the actual path.
Use [NSURL fileURLWithPath:#"/Library/Desktop Pictures"] instead.
Happy coding!