Iphone NSTimer Issue - iphone

Hi I am new to objective c. I am trying to make an app for iphone. I have a button on my view, and the click on which the function playSound is called. This is working properly. It does plays the sound that i want it to.
Now the problem is with the timer. I want the timer to start on the click on the same button, and the timer value will be displayed in a label. I am not very clear with the NSTimer itself either yet. I guess i am doing something wrong here. Can anyone help me with this.
-(IBAction)playSound { //:(int)reps
NSString *path = [[NSBundle mainBundle] pathForResource:#"chicken" ofType:#"wav"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: path];
AVAudioPlayer* theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
theAudio.delegate = self;
[theAudio play];
[self startTimer];
}
- (void)startTimer {
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(targetMethod) userInfo:nil repeats:YES];
labelA.text = [NSString stringWithFormat:#"%d", timer];
}
Using the code above, when i click on the button, it plays the sound, and then my app closes.
Thanks
Zeeshan

This line:
labelA.text = [NSString stringWithFormat:#"%d", timer];
makes absolutely no sense. The timer will call the method you specify as the selector in scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: when it fires so you have to implement that method and update your label there. The first line of startTimer is almost correct, but the selector must include a colon (because it denotes a method that takes one parameter):
- (void)startTimer {
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerFired:) userInfo:nil repeats:YES];
}
Note that I named the selector timerFired: so we have to implement that method. If you want the timer to increment a counter, you will have to do that in this method, too:
- (void)timerFired:(NSTimer *)timer {
static int timerCounter = 0;
timerCounter++;
labelA.text = [NSString stringWithFormat:#"%d", timerCounter];
}
Don't forget to invalidate the timer later when you no longer need it.

Related

iOS Stop Audio Playback At Certain Time (AVAudioPlayer End Time)

I'm wanting to play just part of an audio file. This audio file contains 232 spoken words, I have a dictionary that contains the start time of each word. So I have the start and stop times I just can't find a way to stop at a given time or play the file for a certain duration. Any advice and/or sample code to help me would be much appreciated, thanks Jason.
So I've found a solution, there's a problem with how I get endTime but I sure I can fix it.
//Prepare to play
[audioPlayer prepareToPlay];
//Get current time from the array using selected word
audioPlayer.currentTime = [[wordsDict objectForKey:selectedWord] floatValue];
//Find end time - this is a little messy for now
int currentIndex = 0;
int count = 0;
for (NSString* item in wordsKeys) {
if ([item isEqualToString: selectedWord]) {
currentIndex = count+1;
}
count++;
}
//Store found end time
endTime = [[wordsDict objectForKey:[wordsKeys objectAtIndex:currentIndex]] floatValue];
//Start Timer
NSTimer * myAudioTimer = [NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(checkCurrentTime)
userInfo:nil
repeats:YES]
//Now play audio
[audioPlayer play];
//Stop at endTime
- (void) checkCurrentTime {
if(audioPlayer.playing && audioPlayer.currentTime >= endTime)
[audioPlayer stop];
}
This should do what you want. you don't need a repeating timer that's thrashing the player.
NSString *myAudioFileInBundle = #"words.mp3";
NSTimeInterval wordStartsAt = 1.8;
NSTimeInterval wordEndsAt = 6.5;
NSString *filePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:myAudioFileInBundle];
NSURL *myFileURL = [NSURL fileURLWithPath:filePath];
AVAudioPlayer *myPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:myFileURL error:nil];
if (myPlayer)
{
[myPlayer setCurrentTime:wordStartsAt];
[myPlayer prepareToPlay];
[myPlayer play];
[NSTimer scheduledTimerWithTimeInterval:wordEndsAt-wordStartsAt target:myPlayer.autorelease selector:#selector(stop) userInfo:nil repeats:NO];
}
Try using AVPlayer instead and make use of
addBoundaryTimeObserverForTimes:queue:usingBlock:
What it does: Requests invocation of a block when specified times are traversed during normal playback.
https://developer.apple.com/library/prerelease/ios/documentation/AVFoundation/Reference/AVPlayer_Class/index.html#//apple_ref/occ/instm/AVPlayer/addBoundaryTimeObserverForTimes:queue:usingBlock:
The AVAudioPlayer gives you some neat properties to work with:
currentTime: during playback you can rely on this property.
playAtTime : starts playing from a pre-defined time.
But first of all I would write some helpers for that:
#interface Word {
double startTime;
double endTime;
}
#property double startTime;
#property double endTime;
#end
This is just a class to simply working with the following method.
- (void)playWord:(Word *)aWord {
self.avPlayer.playAtTime = aWord.startTime;
[avPlayer prepareToPlay];
[avPlayer play];
while (avPlayer.playing) {
/*
This while loop could be dangerous as it could go on for a long time.
But because we're just evaluating words, it won't be as much power draining
*/
if(avPlayer.currentTime >= aWord.endTime;
[avPlayer stop];
}
}
I would suggest you to use an array or any other mechanism to automatically switch to the next word. Maybe your could also add a Previous and Next button for handling user input.
Please let me know if that worked for you.
HTH

play animation regardless of any screen iphone

i have some animation to be played at some point in time. that time is decided by some thread running in the backgroud. now i want to play the animation regardless of which screen the user is on. here is screen hierarchy.
login-->homescreen(thread starts running from viewdidload).
from home screen the user may navigate to any other screen and i want the animation to be played. right now i have the animationviewcontrller a membervariable in the homescreen and i am calling it by allocing and initing and pushing and popping when animation is done. and it works only 10% of the time. i have tried performselectoronmainthread and its still the same.
How do i redesign my code such that is plays on anyscreen.
here is my code for my thread.
[self startTimerThread]; // in viewdidload
-(void)startTimerThread
{
homescreenthread = [[NSThread alloc] initWithTarget:self selector:#selector(setupTimerThread) object:nil];
[homescreenthread start];
}
-(void)setupTimerThread
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSTimer* timer = [NSTimer timerWithTimeInterval:5
target:self
selector:#selector(findnewmessages)
userInfo:nil
repeats:YES];
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSRunLoopCommonModes];
//[runLoop addTimer:timer forModes:NSRunLoopCommonModes];
[runLoop run];
[pool release];
}
-(void)findnewmessages
{//finding message from server here. i am using the following request
NSData *serverReply = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *replyString = [[NSString alloc] initWithBytes:[serverReply bytes] length:[serverReply length] encoding: NSASCIIStringEncoding];
//i have omitted lot of other code.
}
use the delegate of the app to display the animation view.
id appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate.m_window addSubview:viewController.view];
Also if you are tying to draw anything on the screen it should be done on main thread.
Please let me know if it helps.

Touchesbegan always fire when using touchesmoved?

I'm new to iPhone programing.
I'm trying to do a simple window with a cat doing two sounds. When you click at the cat icon it should do "miaau", and when wou drag over window (stroke) it should make "mrrrr".
It works, but always when I try to make cat mrrrrr function TouchesBegan fires and cat also do "miaaau".
What to do to make the interface recognize I want only stroke cat, not to touch it for doing the first option, "miaau"?
I suggest to add a NSTimer to touchesBegan method with small time interval (say 0.1 sec):
BOOL tap_event = NO; //ivar declared in header
-(void) touchesBegan:... {
tap_event = YES;
[NSTimer scheduledTimerWithTimeInterval: 0.1 target: self selector: #selector(checkTap:) userInfo: nil repeats: NO];
}
-(void) checkTap:(NSTimer*) t {
if( tap_event ) //miauu here
tap_event = NO;
}
-(void) touchesMoved:... {
tap_event = NO;
//mrrrr here
}
Or as an option check docs for UIGestureRecognizers
This may help you
When does a touchesBegan become a touchesMoved?
touchesBegin & touchesMove Xcode Obj C Question
Max' solution is correct. It will definitely work. I am having an alternative, see this approach.
Make your player object in an .h file then allocate it in viewDidLoad and release in dealloc.
-(void) touchesBegan:... {
//Play the miauu sound.
}
-(void) touchesMoved:... {
[self.yourPlayer stop];
//Play mrrrr.
}
Edit
.h file,
AVAudioPlayer *avPlayer1;
AVAudioPlayer *avPlayer2;
.m file
-(void) touchesBegan:... {
NSString *path = [[NSBundle mainBundle] pathForResource:#"cat" ofType:#"wav"];
avPlayer1 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[avPlayer1 play];
}
-(void) touchesMoved:... {
[avPlayer1 stop];
NSString *path = [[NSBundle mainBundle] pathForResource:#"cat" ofType:#"wav"];
avPlayer2 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[avPlayer2 play];
}
-(void)dealloc
{
[avPlayer1 release];
[avPlayer2 release];
[super dealloc];
}

How to call a method on background thread

in my app i am unzipping a zip file(in splash view controller) later i am displaying an image and firing a timer with 4 secs to change image of the previous image view. and later in view did appear i am firing another timer with 10 secs to call next view controller(home view controller).. and there i have a button to go Story view controller...in this i am using the unzipped data...its taking some time to unzip in this case. so the images r displaying later a few seconds...it fine here
the problem is... i called the unzipping on background thread so its displaying the images quickly and its moving to home view controller..while going to next view controller by clicking a button its crashing on device but its working fine in simulator..
can any body help me out please
Thanks.
code...
-(void)viewWillAppear:(BOOL)animated{
[self performSelectorInBackground:#selector(unXip) withObject:nil];
CGRect imageFrame = CGRectMake(0,20,320,460);
splashView.frame = imageFrame;
splashView.image = [UIImage imageNamed:#"Default.png"];
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(changeImg) userInfo:nil repeats:NO];
[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(changeImg2) userInfo:nil repeats:NO];
}
-(void)unXip{
self.fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
self.documentsDir = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/temp", self.documentsDir];
NSString *filePathbmlg = [NSString stringWithFormat:#"%#/temp/bmlg", self.documentsDir];
NSString *updateURL = [[NSBundle mainBundle] pathForResource:#"bmlg" ofType:#"zip" inDirectory:#"res"];
[fileManager createDirectoryAtPath:filePath
withIntermediateDirectories:NO
attributes:nil
error:nil];
if([fileManager fileExistsAtPath:filePath]) {
NSLog(#"File exists at path: %#", updateURL);
} else {
NSLog(#"File does not exists at path: %#", updateURL);
}
NSLog(#"Checking update at : %#", updateURL);
NSLog(#"Checking filepath at : %#", filePath);
if(![fileManager fileExistsAtPath:filePathbmlg]) {
ZipArchive *zipArchive = [[ZipArchive alloc] init];
if([zipArchive UnzipOpenFile:updateURL]) {
if ([zipArchive UnzipFileTo:filePath overWrite:YES]) {
//unzipped successfully
NSLog(#"Archive unzip Success");
//[self.fileManager removeItemAtPath:filePath error:NULL];
} else {
NSLog(#"Failure To Unzip Archive");
}
} else {
NSLog(#"Failure To Open Archive");
}
[zipArchive release];
}
}
- (void)changeImg{
splashView.image = [UIImage imageNamed:#"screen2.png"];
}
- (void)changeImg2{
splashView.image = [UIImage imageNamed:#"screen3.png"];
}
- (void)viewDidAppear:(BOOL)animated{
[NSTimer scheduledTimerWithTimeInterval:15 target:self selector:#selector(pushCVC) userInfo:nil repeats:NO];
}
- (void)pushCVC{
homeViewController = [[HomeViewController alloc] initWithNibName:#"HomeView" bundle:nil];
[self presentModalViewController:homeViewController animated:YES];
}
It's kind of hard to answer this witout any sample code. One thing to be aware of when using background threads is to make sure that all code that updates the UI runs on the main thread.
Ok, I think this is probably a threading issue. Your unXip method is modifying something on a background thread and then you try to access that "something" from another thread (main thread if you access it from UI code). You need to implement some kind of locking/synchronization mechanism (e.g. NSLock or NSConditionLock).
It looks a bit strange that you are using a timer to call pushCVC. Now, I don't know exactly what your app does but wouldn't it be better to call pushCVC when the unzip has finished by adding this to the end of your unXip method:
[self performSelectorOnMainThread:#selector(pushCVC) withObject:nil waitUntilDone:NO];
and removing the timer.
thanks for your comments and answers....
The problem is the unzipping process is taking a long time...so if i click start button on the home screen before the completion of the unzipping the file, its crashing...because i have to use the data from unzipped file...
Thanks again..

iPhone: How to play a sound at a particular time?

I have written the following code:
-(void)runTimer
{
myTicker=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(showActivity) userInfo:nil repeats:YES];
myTicker = [NSTimer scheduledTimerWithTimeInterval:60.00 target:self selector:#selector(vibrate) userInfo:nil repeats:YES];
}
-(void)showActivity
{
NSDateFormatter *formatter =
[[[NSDateFormatter alloc] init] autorelease];
NSDate *date = [NSDate date];
[formatter setTimeStyle:NSDateFormatterMediumStyle];
[timerLabel setText:[formatter stringFromDate:date]];
}
-(void) vibrate{
AudioServicesPlaySystemSound (kSystemSoundID_Vibrate);
}
I want to play an audio file instead of vibrating the iPhone. Is that possible?
Tangential to the main question but the method called by the timer selector should be of the form...
-(void) aribtaryMethodName:(NSTimer *) theTimer;
...or it may not be called properly. It's selector should look like this:
myTicker=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(aribtaryMethodName:) userInfo:nil repeats:YES];
Check Apple's sample project BubbleLevel. You want the SoundEffect.h and SoundEffect.m into your project and then just call:
mySound = [[SoundEffect alloc]
initWithContentsOfFile:[mainBundle pathForResource:#"mySound"
ofType:#"aif"]];
[mySound play];
Warning: I was able to play only AIF sounds converted via iTunes. Anything else failed, including CAF files. In short: import your whatever sound into iTunes (drag'n'drop), change export to AIF format and then export the file.
Yes. Assume you have a .caf file, you can play it with
SystemSoundID sound_id;
AudioServicesCreateSystemSoundID(NSURL_of_your_caf_file, &sound_id);
...
AudioServicesPlaySystemSound(sound_id);
To play music you may want to use AVAudioPlayer instead.
Sure. See AVAudioPlayer class.
-(IBAction)slider1Changed:(id)sender
{
[timer invalidate];
float sliderValue=slider1.value;
int totalSecond=(int)sliderValue;
time=totalSecond;
int sec= totalSecond %60;
int min= (totalSecond -sec)/60;
sliderValue=(float)totalSecond;
runTime.text=[NSString stringWithFormat:#"%d:%.2d",min,sec];
[slider1 setValue:sliderValue];
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerChanged) userInfo:nil repeats:YES];
// to play the audio from particular time duration
[player setCurrentTime:totalSecond];
}
-(void)timerChanged
{
int totalSecond=[player duration];
float slider1Time=(float)time;
if (time <= totalSecond) {
int sec= time %60;
int min= (time -sec)/60;
runTime.text=[NSString stringWithFormat:#"%d:%.2d",min,sec];
[slider1 setValue:slider1Time];
}
else
{
[player stop];
[timer invalidate];
}
time +=1;
}