NSUserNotification with custom soundName - objective-c

Did anyone manage to make a NSUserNotification soundName to work with a custom sound?
I tried with aif and caf format 44100KHz 16bit 2 second of duration. The notification is displayed at the proper time, with the right title and text, but the default sound gets played instead of my custom sound.
The sound files are correctly copied in the application bundle.
If I try this the sounds work ok:
NSSound* sound = [NSSound soundNamed:#"morse.aif"];
[sound play];
But when I use the same sound in my notification, the default notification sound gets played:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
NSUserNotification* notification = [[NSUserNotification alloc]init];
notification.title = #"Titolo";
notification.deliveryDate = [NSDate dateWithTimeIntervalSinceNow:10];
notification.soundName = #"morse.aif";
[[NSUserNotificationCenter defaultUserNotificationCenter]scheduleNotification:notification];
}
I tried with and without extension, but with no success.
notification.soundName = #"morse.aif";
notification.soundName = #"morse2.caf";
notification.soundName = #"morse";
none of these work.
My application is not signed and not sandboxed, but I don't think that's necessary for user notifications, and apart from the sound problem the notifications work great.

It seems to me like this issue is case-sensitivity. Using notification.soundName = #"Morse"; works perfectly for me. As you can see in the NSSound documentation for soundNamed The search folders for sounds are, in order:
~/Library/Sounds
/Library/Sounds
/Network/Library/Sounds
/System/Library/Sounds
If you look in the last one, which is probably where you're trying to pull from since they're the sounds in System Preferences, you can see their names
So keep the case of the file, and omit the extension and it should work as expected.

If you have multiple versions of your App binary notification center may be searching the wrong binary for your sound files.
If you make sure to delete any old copies of the binary it should fix the issue.
From the Apple Dev Forums: https://devforums.apple.com/message/708511
This can happen if you have multiple version of your app binary floating around. NotificationCenter only fines one of them. If it is the one without the sound then it will not work.

Related

System sound effect for copy, paste and delete operations

Does anyone know how to play the system sound effects for (drag) copy, paste and delete operations on OS X? I mean the sound that Finder uses when moving files. I didn't find any API to do that. Does someone maybe know where those sound files are located? I only found the system alert sounds (Frog, Submarine etc..).
SystemSounds are available in
/System/Library/Components/CoreAudio.component/Contents/Resources/SystemSounds/ location.
NSSound *systemSound = [[NSSound alloc] initWithContentsOfFile:#"/System/Library/Components/CoreAudio.component/Contents/Resources/SystemSounds/finder/move\ to\ trash.aif" byReference:YES];
if (systemSound) {
[systemSound play];
}
/System/Library/Components/CoreAudio.component/Contents/Resources/CoreAudioAUUI.bundle/Contents/Resources

iOS settings toggle switch works the first time but not again

I am using the iPad settings app to change some button sounds and a background image. It all works well and the settings are maintained from one app launch to another in the simulator. Now I have implemented a toggle switch to either set sets of sounds off or on. When the app launches, whatever state the switch is in, it works; e.g. if the "Alert Sounds" switch is OFF the alert sounds are silent and if I change it to ON the sounds will start working. However, if I turn the switch back OFF the sounds still keep working. However, if the state is ON when the app launches, the sounds work, but will not be silenced when the switch is set to OFF.
Note that this is different than the settings not taking effect until a second round of settings. That was a previous problem I solved (thanks to stack overflow) by using:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[NSUserDefaults standardUserDefaults] synchronize];
}
I have methods named:
- (void)defaultsChanged:(NSNotification *)NSUserDefaultsDidChangeNotification
(which is called when the notification is sent)
and
-(void)setValuesFromPreferences
(which is called in ViewDidLoad)
The logic looks like this in both:
// Set alert sounds from preferences
NSString *alertSoundPreference = [userDefaults stringForKey:kAlertSound];
BOOL alertSoundEnabled = [userDefaults boolForKey:kAlertSoundEnabled];
if (alertSoundEnabled)
{
// Create the URLs for the alert audio files
// Store the alert sound URLs as a CFURLRef instances
// Create system sound objects representing the alert sound files
}
I do not have an else, because I assume that no sound resources will be specified if alertSoundEnabled is NO.
I have searched for explanations and tutorials that mention this problem but have not found any yet, so I'm asking here. Thanks for any suggestions.
viewDidLoad is not necessarily called when the app becomes active again (nor does viewWill/DidAppear, IIRC), as the whole point of iOS 4+ multitasking is to prevent such loading/unloading and recreation of objects on app-switching.
If I had to guess, the sounds are already allocated when the user had the switch ON at original launch/viewDidLoad; however, if your code does nothing to explicitly disassociate them when it loads back up, they would continue playing, as they are all already set up.
As such, I'd try adding an else clause that (upon alertSoundEnabled == NO) destroys your system sound objects.

Using iPodMusicPlayer without giving control to iPod in background

I'm developing an app with a very specific focus on shuffling songs within a selected playlist. When the app launches, it lists all the user's playlists, and then when one is selected it loads it into my music player view and get's going. I'm using an MPMusicPlayerController iPodMusicPlayer to handle doing this because I'm pretty new at iOS development and it handles backgrounding and everything nice and easily for me. I want music to continue playing in the background and I like how whatever I change within my app is also reflected in the iPod app. However, I don't want the user to be able to change things in the iPod app and then return to mine. Since my app is all about shuffling, the user could easily go into iPod and disable shuffle, then come back to my app and the experience is ruined. I also don't want them to change the playlist from within iPod.
Can anyone help me think of a way to accomplish this? Play/pause/skip/prev/volume is no problem from within iPod (I want that to be possible), but I don't want changing the song queue to be possible. Is there a way I could add a handler in my app that catches if the user changed something while my app was in the background (and if so, stop music and reset my app to the initial screen)? I'd really appreciate any suggestions. I'm hoping something like this is possible before I venture into using AVPlayerQueue or the like (as a side note, can you play DRM'd files with AVPlayerQueue?).
What I would do is set a bool when backgrounded:
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setBool:YES forKey:#"BACKGROUNDED"];
Then, in your handler for the MPMusicPlayerControllerNowPlayingItemDidChangeNotification that you should be implementing so that you have that control, do the following:
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
if ([prefs boolForKey:#"BACKGROUNDED"]) {
[prefs setBool:NO forKey:#"BACKGROUNDED"];
exit(0);
}
It might be too abrupt, depending on when it checks this code (I don't know when it does in your project) but that should accomplish your goal. exit(0) is the way to terminate your app without throwing an exception. Is that helpful?

How do I get the icon of the user's Mac?

Using Objective-C and Cocoa, does anyone know how to get the icon for a user's computer (the one that shows under "Devices" and "Network" in Finder)? Not the harddisk icon, the actual one for a user's device. It ranges from a MacBook icon to the Mac Pro icon to a Windows blue screen of death monitor icon.
I've tried stuff along the following lines:
NSImage *icon = [[NSWorkspace sharedWorkspace]
iconForFileType: NSFileTypeForHFSTypeCode(kComputerIcon)];
But that just returns the same icon all the time, obviously. I've also tried the iconForFile: method but I don't know of a file path to use as the parameter. Can anybody point me in the right direction?
[NSImage imageNamed: NSImageNameComputer]
This will return the icon of the current computer
Another place to look for icons:
/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources
You can create NSImage objects with the files in there like this:
[[NSImage alloc] initWithContentsOfFile:#"/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/com.apple.macbook-unibody.icns"];
It's probably not recommended to hard-code the value like that, however, since Apple may change the icons' locations. There is a file called IconsCore.h that contains many other constant values such as 'kToolbarDesktopFolderIcon' which can be used as follows:
[[NSWorkspace sharedWorkspace] iconForFileType: NSFileTypeForHFSTypeCode(kToolbarDesktopFolderIcon)];
I believe these constants only work in Snow Leopard, though.
If you are looking for any other system icons check out Apple's sample project called "IconCollection". http://developer.apple.com/mac/library/samplecode/IconCollection/listing5.html
The sample comes with a plist file that has the names and codes for quite a few system icons that can be accessed using;
OSType code = UTGetOSTypeFromString((CFStringRef)codeStr);
NSImage *picture = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(code)];
where codeStr is the string code for the icon provided in icons.plist

Can I disable UIPickerView scroll sound?

I want to disable the annoying clicks that the UIPickerView generates upon scrolling up and down. Is there a way to do this? I want to play short sounds for each item that the picker view lands upon. It gets ruined by the built in sound.
I understand that the picker sounds can be turned off globally by switching off the keyboard sounds in iPhone/iPod settings. But is there a way to programatically do this?
Any help will be much appreciated!
Thanks
I've been struggling with a UIPickerView sound issue, and even though it's only partially relevant to the original question, I'm posting the problem/solution here because this topic keeps coming up in my search results so I think anyone else in the same boat may end up here too…
I needed to initialize a UIPickerView to restore the currently selected row from saved data. Simple, right? In viewDidLoad, just call the selectRow:inComponent:animated method of UIPickerView:
[myPicker selectRow:currentRowIndex inComponent:0 animated:NO];
This works as expected, but has a side effect that it generates a single "click" sound as if the user had scrolled the control. The click sound only occurs when running on a device (not the simulator), and only if the device has iOS 3.x installed (I tested with 3.1.3 and 3.2). This was apparently a bug in iOS that was fixed starting with iOS 4.0. But if you need to target Gen1 iPhone, you're stuck with iOS 3.1.3 where this problem is present.
I discussed the issue with Apple DTS, but they were unable to suggest any workaround other than upgrading to 4.0. I asked if they would make an exception and permit the use of the undocumented setSoundsEnabled mentioned above (which does actually solve the problem). The answer was, "There are no exceptions."
After some additional detective work, I discovered that you can prevent the sound from occurring by temporarily removing the UIPickerView from the superview, call selectRow, then re-add it to the superview. For example, in viewDidLoad:
UIView *superview = [myPicker superview];
[myPicker removeFromSuperview];
[myPicker reloadAllComponents];
[myPicker selectRow:currentRowIndex inComponent:0 animated:NO];
[superview addSubview:myPicker];
This gets rid of the extraneous click sound without using undocumented/private APIs so should pass Apple's approval process.
After using this specific undocumented api for over a year on the App Store Apple finally asked me to remove it from my App. It is very frustrating for audio apps to have that damn click sound. The best advice is to share with users that the picker sound can be disabled globally in the settings application under "Sounds" and setting "Keyboard Clicks" to "Off". I also strongly recommend visiting https://bugreport.apple.com/ and filing a bug for UIPickerView, as it can cause distortion in audio applications when the picker click is played.
they have just rejected an app of mine because the use of undocumented api's...thats one of them.
Someone I know says he got this past the App Store review just last week:
// Hide private API call from Apple static analyzer
SEL sse = NSSelectorFromString([NSString stringWithFormat:#"%#%#%#", #"set",#"Sounds",#"Enabled:"]);
if ([UIPickerView instancesRespondToSelector:sse]) {
IMP sseimp = [UIPickerView instanceMethodForSelector:sse];
sseimp(self.thePicker, sse, NO);
}
There is an undocumented way (I'm actually not sure if it is still available in iphone 3.0) but here it is any way
#import <UIKit/UIKit.h>
#interface SilintUIPickerView: UIPickerView
{ }
- (void) setSoundsEnabled: (BOOL) enabled;
#end
use this subclass instead and call [view setSoundsEnabled: NO]
I'm interested in knowing how it goes in the latest SDK, give it a shot and let us know.
Could this trick work? Someone was able to suppress the camera shutter sound effect by playing an inverted copy of the sound at the same moment: https://stackoverflow.com/a/23758876/214070
Maybe this not the answer for this particular question, but I had a similar problem - set minimumDate for datePicker, and I wanted set it without annoying "click" sound. After some time found very simple solution:
datePickerCustomTime.minimumDate = [[NSDate date] dateByAddingTimeInterval:300]// min time to set = now + 5 min
[datePickerCustomTime setDate:[[NSDate date] dateByAddingTimeInterval:300] animated:NO];
I found small quickie solution for this try below
UIPickerView *pickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, yPickerView, VIEW_WIDTH, PICKERVIEW_HEIGHT)];
pickerView.delegate = self;
pickerView.dataSource = self;
pickerView.showsSelectionIndicator = YES;
pickerView.alpha = 0.8f;
pickerView.tag = fieldTag;
[pickerView selectRow:pickerViewSelectedIndex inComponent:0 animated:NO];
set the animated:NO for selectRow: method