i want to play a sound file when alertview appears and plays continuously till user clicks on ok or cancel.how do i do this?
As Zoul says, you set-up and play your sound as you call [myAlert show] and cancel the sound in the alert view callback. Your code will look something like this:
AVAudioPlayer *myPlayer;
// ...
// create an alert...
NSError *error;
myPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:mySoundFileURL error:&error];
// handle errors here.
[myPlayer setNumberOfLoops:-1]; // repeat forever
[myPlayer play];
[myAlert show];
// ...
// in alert callback.
[myPlayer stop];
[myPlayer release];
As you already call the show method to display the dialog, why don’t you simply start playing the sound there and stop in the alert view callback? For the sound itself you can use AVAudioPlayer.
Related
I am developing an iOS 5.1 application on Xcode 4.2.
I have a uitablcontroller with different tabs. My problem is when a tab is clicked , the application 'freezes' for few seconds and does all the codes it's meant to do, but it does not load the UIAlertView first as it should be.
I have the UIAlertView declared in the viewDidLoad.
Here is a code snippet:
- (void)viewDidLoad
{
NSLog(#"##### VIEW DID LOAD 1 #####");
// Display Alert: Loading
alertView = [[UIAlertView alloc] initWithTitle:#"Loading"
message:#"\n"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:nil];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
spinner.center = CGPointMake(139.5, 75.5); // .5 so it doesn't blur
[alertView addSubview:spinner];
[spinner startAnimating];
[alertView show];
[super viewDidLoad];
NSLog(#"##### VIEW DID LOAD 2 #####");
self.tableView.dataSource = self;
self.tableView.delegate = self;
[self callMainMethod];
}
When the tab is clicked, I can see that the first NSLog's are displayed in the Log, and then the main method is called, but the UIAlertview is not displayed.
When viewDidLoad is running, it may be that the frame of the associated UIView has zero size. I don't know, but the UIAlertView may be trying to present itself in this zero-size frame. Does it make sense if you present the UIAlertView in viewDidAppear?
If [self callMainMethod] is taking lots of compute power, then the display might not be updated until it finishes. You could try moving it to viewDidAppear. You could also try delaying it, so that the main run loop for the UI thread, the thread that the display is updated on and the thread that executesviewDidLoad and all the other view... methods, has time to complete everything and become idle before you start the heavy processing. It's only when the run loop has done all the processing it can that it starts actually to update the display. Like this:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 200000000), dispatch_get_main_queue(), ^{
[self callMainMethod];
}
The documentation for that is at http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
If that suspicion is correct, you should then get the UIAlertView popping straight up, only to have everything go dead for a while, while callMainMethod executes.
I wish I knew how to write, "dispatch this block only after you've managed to finish updating the display with everything up to here", but I don't know how to do that. So the dispatch call above should delay the call to callMainMethod by 200ms, which is usually plenty.
If that works, you should probably start another question, something like "How can I stop the display freezing while I execute this method."
I'm trying to get a custom sound working on a UILocalNotification, and I'm just getting no sound at all. If I use UILocalNotificationDefaultSoundName, I indeed get the default sound, but when the custom sound is specified, there is no sound, just the message. The sound is less than 30 seconds and it's in the right format, as far as I can tell. Here's a screenshot of the file info:
I've inspected the .app directory in XCode's DerivedData directory, and the alarm.caf file is at the root of the app, which I believe means it's in the bundle (right?).
I'm pretty sure this was working a while ago, and I've since upgraded Xcode. Maybe that is a hint?
I've also tried deleting/reinstalling/rebooting as mentioned in other answers. As you can see, I'm calling cancelAllLocalNotifications first.
Does anyone have any idea what could be wrong?
[[UIApplication sharedApplication] cancelAllLocalNotifications];
NSLog(#"installing alarm");
[arguments pop]; // name
[arguments pop]; // title
alarm.alertBody = [arguments pop];
alarm.fireDate = [[NSDate date] addTimeInterval:[[arguments pop] intValue]/1000];
//alarm.soundName = UILocalNotificationDefaultSoundName;
alarm.soundName = #"alarm.caf";
[[UIApplication sharedApplication] scheduleLocalNotification:alarm];
Your code seems to be good.
Try to clean your project, uninstall your app from your device/simulator, then re-install it. It could help maybe :)
I don't know the reason (and I didn't read documentation too), I just turned on the action property notification setHasAction:YES and the sound began to play.
please make sure that the iPhone is not in silent mode( because your code seems to be good )
just check the button on the side of your iPhone
Ok, so here's what happened. I forgot how the app handles the notification itself if it is still running. My code was only displaying a UIAlertView and not playing the sound. I'm not sure why it worked with the default sound. In any case, I added code like this to my AppDelegate:
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
NSLog(#"didReceiveLocalNotification");
if (application.applicationState == UIApplicationStateActive) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"MarkMyTime"
message:notification.alertBody
delegate:self cancelButtonTitle:#"OK"
otherButtonTitles:nil];
NSString *soundFilePath = [[NSBundle mainBundle]
pathForResource:notification.soundName ofType:nil];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL: fileURL error: nil];
[fileURL release];
player.delegate = self;
[player prepareToPlay];
[player play];
[alertView show];
if (alertView) {
[alertView release];
}
}
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
NSLog(#"Releasing player");
[player release];
}
This will show a UIAlertView and play the sound on the notification object. You also need to add the AVAudioPlayerDelegate interface to the AppDelegate to be able to assign the delegat to the player. I think if you are using ARC, this code could be simplified a bit.
#interface AppDelegate : PhoneGapDelegate <AVAudioPlayerDelegate> {
I'm not sure if this is the best approach, so feel free to chime in with any improvements.
Maybe you do not add the sound file (*.caf) in Xcode project: Build Phases/Copy Bundle Resources.
Your code is good, but check your iPhone setting
setting -> Notification center -> Your App -> Sound - > "On"
the sound should be "On".
So, to enable this, checked Inter App Audio at Capabilities in Targets of the application and it was Off Capabilities in Inter-app audio
change this to On.
Then local notification sound is working.
I have spent days researching on SO and other websites for the answer to this but without any luck.
Essentially, the challenge I've set for myself is to create an alarm clock app for iOS that will sound no matter where the user may be (foreground or background). This I have already accomplished by using an AVAudioPlayer instance and starting to play an empty sound file when the user sets the alarm in order for the app to keep running in the background. When it is time for the alarm to go off (ie when the NSTimer is fired) a second player, which has already been initiated and prepared to play, starts playing the ringtone to which the user wakes up.
Also, I have managed to handle interruptions by a phone call, system timer or alarm clock by implementing the AVAudioSessionDelegate methods beginInterruption and endInterruptionWithFlags. It works both in background and foreground modes, but the strangest thing happens:
When the interruption ends, the AVAudioPlayer resumes playing BUT I cannot execute any other code in my app unless I bring the app to the foreground again.
To get to the bottom of this, I have experimented with a much simpler project which I am posting below.
What this app does is, as soon as you enter the app, an instance of the AVAudioPlayer class starts looping a certain sound. Then when you bring it to the background, the player continues to loop the sound. When an interruption occurs I pause the player and when it ends I use a dispatch to wait a couple of seconds before it calls two methods, ie (void)playPlayer, a method that contains the code to resume playing the file and (void)tester, a method that contains a timer, which is set to stop the player 5 seconds after the interruption (or 7 seconds to be exact) has ended. Both the methods get called as indicated by the NSLogs I have put in both of them, but the timer never gets fired and the player continues to play indefinitely.
Here is the code for the .h file:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
#interface InterruptionTest3ViewController : UIViewController <AVAudioSessionDelegate, AVAudioPlayerDelegate>
{
AVAudioSession *mySession;
AVAudioPlayer *myPlayer;
}
-(void) playPlayer;
-(void) pausePlayer;
-(void) tester;
#end
Here is the code for the .m file:
#import "InterruptionTest3ViewController.h"
#interface InterruptionTest3ViewController ()
#end
#implementation InterruptionTest3ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
mySession = [AVAudioSession sharedInstance];
NSError *setActiveError = nil;
[mySession setActive:YES withFlags:AVAudioSessionSetActiveFlags_NotifyOthersOnDeactivation error:&setActiveError];
if (setActiveError) {
NSLog(#"Session failed to activate within viewDidLoad");
}
else {
NSLog(#"Session was activated within viewDidLoad");
}
NSError *setCategoryError = nil;
[mySession setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
if (setCategoryError) {
NSLog(#"Category failed to be set");
}
else {
NSLog(#"Category has been set");
}
[mySession setDelegate:self];
NSString *path = [[NSBundle mainBundle] pathForResource:#"headspin" ofType:#"wav"];
NSError *initMyPlayerError = nil;
myPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&initMyPlayerError];
if (initMyPlayerError) {
NSLog(#"myPlayer failed to initiate");
}
else {
NSLog(#"myPlayer has been initiated");
}
[myPlayer prepareToPlay];
[self playPlayer];
OSStatus propertySetError = 0;
UInt32 allowMixing = true;
propertySetError = AudioSessionSetProperty (
kAudioSessionProperty_OverrideCategoryMixWithOthers,
sizeof (allowMixing),
&allowMixing
);
[myPlayer setNumberOfLoops:-1];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
-(void) beginInterruption
{
[myPlayer pause];
}
-(void) endInterruptionWithFlags:(NSUInteger)flags
{
if (flags) {
if (AVAudioSessionInterruptionFlags_ShouldResume)
{
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC),dispatch_get_main_queue(), ^{
[self playPlayer];
[self tester];
});
}
}
}
}
-(void) tester
{
[NSTimer timerWithTimeInterval:5.0 target:self selector:#selector(pausePlayer) userInfo:nil repeats:NO];
NSLog(#"tester method has been called");
}
-(void) playPlayer
{
[NSTimer timerWithTimeInterval:5.0 target:myPlayer selector:#selector(stop) userInfo:nil repeats:NO];
[myPlayer play];
NSLog(#"playPlayer method has been called");
}
-(void) pausePlayer
{
[myPlayer pause];
}
//viewDidUnload etc not listed.
So, this is it folks. Again, why is the timer not being fired after an interruption while the app is in the background? Do I need to set something in the applicationDidEnterBackground method?
Thank you very much in advance!
please use "[NSTimer scheduledTimerWithTimeInterval:5.0 target:myPlayer selector:#selector(stop) userInfo:nil repeats:NO]";
Not to avoid the question, but the play-an-empty-sound-in-the-background bit is a hack. The ability to play a sound in the background is provided so that you can play music or other sound for the user's benefit, not to keep a background process alive.
Consider using local notifications instead if you want to play a sound or otherwise alert the user at a particular time.
why is the timer not being fired after an interruption while the app is in the background?
If I remember correctly, timers are either suspended or cancelled when your app enters the background. The documentation explicitly says that you should "stop timers and other periodic tasks" when your app is interrupted, i.e. when it's sent to the background.
I used AVQueuePlayer to play several items in background. And code worked perfect in iOS4. And in iOS5 AVQueuePlayer changed its behavior, so player stops playing after first item is ended.
Matt Gallagher wrote a hint in this post. "As of iOS 5, it appears that AVQueuePlayer no longer pre-buffers. It did pre-buffer the next track in iOS 4."
So my question is how to play several items in background using AVPlayer or AVQueuePlayer in iOS5.
Matt Gallagher's answer in his blog:
"You must observe the current item in the AVQueuePlayer. When it changes, you must use UIApplication to start a backgroundTask and only end the background task when you receive a ready to play notification for the next file."
Actually, this did not helped me.
So my solution is:
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
I can't explain why, but this line of code put before adding new AVPlayerItem made my code work.
Also for those one who adds next track in background and uses method
- (void)loadValuesAsynchronouslyForKeys:(NSArray *)keys completionHandler:(void (^)(void))handler;
You must add AVPlayerItem to player on the main thread.
like this:
- (void)addAsset:(AVAsset*)as
{
[player insertItem:[AVPlayerItem playerItemWithAsset:as] afterItem:[player currentItem]];
}
.........
//adding new track
AVURLAsset* as = [[self createAsset:urlString] retain];
NSArray *keys = [NSArray arrayWithObject:#"tracks"];
[as loadValuesAsynchronouslyForKeys:keys completionHandler:^(void) {
NSError *error =
AVKeyValueStatus trackStatus = [as statusOfValueForKey:#"tracks" error:&error];
switch (trackStatus) {
case AVKeyValueStatusLoaded:
[self performSelectorOnMainThread:#selector(addAsset:) withObject:as waitUntilDone:YES];
[as release];
break;
case AVKeyValueStatusFailed:
[as release];
break;
case AVKeyValueStatusCancelled:
[as release];
break;
default:
break;
}
}];
UPDATE:
Matt Gallagher was right, but it works only if you do not use asynchronous loading.
I am building an application for iPod.
When running it, the native iPod music app should continue play in background, but this is not happening.
I think it might have something to do with the sound that it is played by my app. For playing a short sound at a certain point, I am using AVAudioPlayer. The code looks like this:
NSString *path = [[NSBundle mainBundle] pathForResource:#"sound" ofType:#"mp3"];
NSLog(#"%#", path);
player = [[AVAudioPlayer alloc] initWithData:[NSData dataWithContentsOfFile:path] error:nil];
[player prepareToPlay];
[player play];
Could this interfere with the native music player on iPod?
And another thing. In my app, I have integrated AdWhirl and it appears to use AudioToolbox.
Thanks a lot,
George
You must set up your AVAudioSession to use the Ambient category. This tells the system that your sound should blend with any already playing sound. The best way to do this in iOS 4 is in the app delegate, like this:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
// ...
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// reactivate audio session
[[AVAudioSession sharedInstance] setActive: YES error: nil];
}
Read up on Audio Sessions. If your app produces sound, you always want be conscious of and in control of your audio session.