Value stored to seconds during its initialization is never read - iphone

float seconds = audioPlayer.currentTime;
if ((seconds = 11)){
[self performSelector:#selector(viewController) withObject:nil];
} else {
if ((seconds = 23)){
[self performSelector:#selector(secondViewController) withObject:nil];
} else {
}
Anyidea how to fix this message in blue when i just want to see if audioplayer current playback time is 11 then do this or 23 then do that

float seconds = audioPlayer.currentTime;
if (seconds == 11.0) {
// Do something
} else if (seconds == 23.0) {
// Do something else
} else {
// Do something else
}

It's probably late and you only need a second pair of eyes, happens all the time :) Try this:
float seconds = audioPlayer.currentTime;
if ((seconds == 11.0)){
[self performSelector:#selector(viewController) withObject:nil];
} else {
if ((seconds == 23.0)){
[self performSelector:#selector(secondViewController) withObject:nil];
} else {
}

Related

GameKit achievement questions

My game center achievements are appearing in Game Center, so I know my implementation is correct in reporting.
Couple of questions on that.
First, in Game Center it is not showing the percentage view on the image... ie 2% complete next to the achievement, even though I have reported .02. I know the achievement is being reported because if I throw the 100 at it, it records the achievement.
Second, my achievements are not appearing to the user upon reward. As I understood, this functionality was suppose to be handled automatically by gamekit. I was under the impression the the small modal would appear letting the user know they completed an achievement. I now think there is something I have to do, because no small modal is appearing.
I will attach my code, but most of it stock.
My last problem is retrieving scores. I believe I am going to have to store my own scores because my current implementation does not look like it will mesh well.
Thanks in advance...
- (void) loadAchievements
{ [GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
if (error != nil)
{
// handle errors
}
if (achievements != nil)
{
// process the array of achievements.
}
}];
}
-(float)getAchievementPercentageForIdentifier:(NSString *)identifier {
__block float percentage = 0;
[GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
if (error != nil)
{
// handle errors
}
if (achievements != nil)
{
// process the array of achievements.
for (GKAchievement *achievement in achievements) {
if ([achievement.identifier isEqualToString:identifier]) {
percentage = achievement.percentComplete;
NSLog(#"percent complete --> %f", achievement.percentComplete);
}
}
}
}];
NSLog(#"Percentage --> %f", percentage);
return percentage;
}
- (void) reportAchievementIdentifier: (NSString*) identifier percentComplete: (float) percent
{
GKAchievement *achievement = [[[GKAchievement alloc] initWithIdentifier: identifier] autorelease];
if (achievement)
{
achievement.percentComplete = percent;
[achievement reportAchievementWithCompletionHandler:^(NSError *error)
{
if (error != nil)
{
// Retain the achievement object and try again later (not shown).
}
}];
}
}
-(void) addCompletedGameToAchievements {
float oneGamePercentage = 0;
float tenGamePercentage = 0;
float fiftyGamePercentage = 0;
float hundredGamePercentage = 0;
float fivehundredGamePercentage = 0;
float thousandGamePercentage = 0;
int gamesComplete = 0;
oneGamePercentage = [self getAchievementPercentageForIdentifier:kAchievementGamesCompletedOne];
tenGamePercentage = [self getAchievementPercentageForIdentifier:kAchievementGamesCompletedTen];
fiftyGamePercentage = [self getAchievementPercentageForIdentifier:kAchievementGamesCompletedFifty];
hundredGamePercentage = [self getAchievementPercentageForIdentifier:kAchievementGamesCompletedHundred];
fivehundredGamePercentage = [self getAchievementPercentageForIdentifier:kAchievementGamesCompletedFivehundred];
thousandGamePercentage = [self getAchievementPercentageForIdentifier:kAchievementGamesCompletedThousand];
if (oneGamePercentage != 100) {
[self reportAchievementIdentifier:kAchievementGamesCompletedOne percentComplete:100];
}
if (tenGamePercentage != 100) {
gamesComplete = tenGamePercentage * 10;
gamesComplete++;
[self reportAchievementIdentifier:kAchievementGamesCompletedTen percentComplete:(gamesComplete * .10)];
}
if (fiftyGamePercentage != 100) {
gamesComplete = fiftyGamePercentage * 50;
gamesComplete++;
NSLog(#"fifty game reported %f ",(gamesComplete * .02));
[self reportAchievementIdentifier:kAchievementGamesCompletedFifty percentComplete:(gamesComplete * .02)];
}
if (hundredGamePercentage != 100) {
gamesComplete = hundredGamePercentage * 100;
gamesComplete++;
[self reportAchievementIdentifier:kAchievementGamesCompletedHundred percentComplete:(gamesComplete * .01)];
}
if (fivehundredGamePercentage != 100) {
gamesComplete = fivehundredGamePercentage * 500;
gamesComplete++;
[self reportAchievementIdentifier:kAchievementGamesCompletedFivehundred percentComplete:(gamesComplete * .002)];
}
if (fivehundredGamePercentage != 100) {
gamesComplete = thousandGamePercentage * 1000;
gamesComplete++;
[self reportAchievementIdentifier:kAchievementGamesCompletedThousand percentComplete:(gamesComplete * .0001)];
}
NSLog(#"100 game percentage -- > %f", hundredGamePercentage);
}
Many problems...
I'm confused by the logic behind incrementing gamesComplete
You should store them locally instead of asking GameCenter every time.
-getAchievementPercentageForIdentifier: will always return 0 because GameKit methods are asynchronous.
GKAchievement.percentageComplete is a percentage. You need to multiply by 100.
I think you have to do your own achievement notifications.

UIScrollView With next and previous button Action

i am new in iphone,i am using horizontal scrollview these are horizontally scrolling properly
but i want these horizontally scrolling on previous and next button action.please help me asap.
Thanks:)
Here are the steps that was worked for me
in .h file write following code.
int scrollMove;
UIScrollView *aScrView;
-(IBAction)nextBtnAction:(id)sender;
-(IBAction)previousBtnAction:(id)sender;
in .m file
- (void)viewDidLoad
{
[super viewDidLoad];
scrollMove=50;
aScrView=[[UIScrollView alloc]init];
aScrView.frame=CGRectMake(25,50, 270, 50);
aScrView.delegate=self;
aScrView.contentSize=CGSizeMake(1200, 0);
[self.view addSubview:aScrView];
// Do any additional setup after loading the view from its nib.
}
-(IBAction)nextBtnAction:(id)sender{
float coordinate = 2.0f;
//scrollMove=50;
[aScrView setContentOffset:CGPointMake(scrollMove * coordinate, 0) animated:YES];
scrollMove=scrollMove+50;
}
-(IBAction)previousBtnAction:(id)sender{
float coordinate = 1.0f;
[aScrView setContentOffset:CGPointMake(scrollMove * coordinate, 0) animated:YES];
scrollMove=scrollMove-50;
}
Hope this helps you!!!
Use this one when you want to go on next visible content of Scroll view:
(Put this one inside next button method)
float coordinate = 2.0f;
[nodeScrollView setContentOffset:CGPointMake(460 * coordinate, 0) animated:YES];
move on previous visible content view:
float coordinate = 1.0f;
[nodeScrollView setContentOffset:CGPointMake(460 * coordinate, 0) animated:YES];
(Put this one inside previous button method)
**You need some mathematical calculation with coordinate
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * pageNumberYouWantToGoTo;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:YES];
- (void)viewDidLoad
{
x=0;
y=320;
}
- (IBAction)next:(id)sender
{
y=320;
[scrl_Knot setContentOffset:CGPointMake(x+320,0) animated:YES];
x+=320;
}
- (IBAction)pre:(id)sender
{
x=0;
[scrl_Knot setContentOffset:CGPointMake(y,0) animated:YES];
y-=320;
}
- (IBAction)pre:(id)sender
{
btnNext.enabled = TRUE;
imageID--;
[scrl_venuelist setContentOffset:CGPointMake(imageID*273, 0) animated:YES];
pagecontrol.currentPage=imageID;
if(imageID <= 0)
{
btnPrevious.enabled = FALSE;
}
}
- (IBAction)next:(id)sender
{
btnPrevious.enabled = TRUE;
imageID++;
[scrl_venuelist setContentOffset:CGPointMake(imageID*273, 0) animated:YES];
pagecontrol.currentPage=imageID;
if(imageID >= imageArr.count-1)
{
btnNext.enabled = FALSE;
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
{
imageID = scrollView.contentOffset.x / scrollView.frame.size.width;
pagecontrol.currentPage=imageID;
if(imageID <= 0)
{
btnPrevious.enabled = FALSE;
imageID = 0;
}
else
{
btnPrevious.enabled = TRUE;
}
if(imageID >= imageArr.count-1)
{
btnNext.enabled = FALSE;
imageID = imageArr.count-1;
}
else
{
btnNext.enabled = TRUE;
}
}
- (void)viewDidLoad
{
scrl_venuelist.delegate=self;
scrl_venuelist.contentSize =CGSizeMake(273 * [imageArr count], 137);
if(imageID == 0)
{
btnPrevious.enabled = FALSE;
if(imageID == imageArr.count-1)
{
btnNext.enabled = FALSE;
}
}
else if(imageID == imageArr.count-1)
{
btnNext.enabled = FALSE;
}
else if(imageID > imageArr.count-1)
{
imageID = [imageArr count]-1;
}
scrl_venuelist.pagingEnabled=YES;
pagecontrol.numberOfPages=[imageArr count];
pagecontrol.currentPage=0;
}
If Any one Looking for way of doing in C# for Xamarin
public int CurrentIndex
{
get => (int)Math.Round(this.scrollView.ContentOffset.X / this.scrollView.Frame.Width);
set => this.scrollView.ScrollRectToVisible(new CGRect(new CGPoint(this.scrollView.Frame.Size.Width * value, 0), this.scrollView.Frame.Size), true);
}
This getter and Setter should provide you current page as well let you scroll to the specified page
On Button Actions Just Update Current Index to new Values, Which could current Index +1/-1.

xcode defining variable problem

Can someone please explain to me why my variable called activity is 0 when defined as a float, but displays the correct value when defined as an int? I have another float which is used for pretty much the same thing but works perfectly!
#implementation AppPrefsViewController
float age;
int activity;
...
-(void) updateAValues{
if (selectedActivityUnit == 0){
activity = 50;
NSLog(#"activity = %d",activity);
}
if (selectedActivityUnit == 1){
activity = 75;
NSLog(#"activity = %d",activity);
}
}
....
- (void)updateLabel {
if (selectedUnit == 0) {
subView.hidden = YES;
age = 1;
NSLog(#"age = %d",age);
}
if (selectedUnit == 1) {
subView.hidden = YES;
age = 2;
NSLog(#"age = %d",age);
}
if (selectedUnit == 2) {
subView.hidden = NO;
age = 3;
NSLog(#"age = %d",age);
}
}
You're using
NSLog(#"activity = %d", activity);
To display your values. This works if activity is an int type as %d is for displaying ints. If you want to display floats you need the float formater %f, as in:
NSLog(#"activity = %f", activity);

MPMoviePlayerController play error on iPad

I'm writing an app that will be playing videos alot using the MPMoviePlayerController on the iPad. The problem is the app was working fine and playing the videos when I stopped working about 15 hours ago but now the videos do not play. The MPMoviePlayerController will show the first frame from the video, and in full screen view I can scrub through the movie fine but when I hit play it just pauses right away. I have the code below, when debugging i noticed that when I call play it sends an MPMoviePlayerPlaybackStateDidChangeNotification with the playbackState being MPMoviePlaybackStatePlaying and then right away it sends another MPMoviePlayerPlaybackStateDidChangeNotification notification with playbackState being MPMoviePlaybackStatePaused. Not sure if that helps but please let me know if you see anything wrong in my code or have an ideas, thanks.
- (void)handleNotification:(NSNotification *)notification {
if ([[notification name] isEqualToString:MPMoviePlayerPlaybackStateDidChangeNotification]) {
if (_videoPlayer.playbackState == MPMoviePlaybackStatePlaying) {
_playButtonLarge.hidden = YES;
_scrubber.maximumValue = _videoPlayer.duration;
[_playPauseButton setBackgroundImage:[UIImage imageNamed:#"video_controls_pause.png"] forState:UIControlStateNormal];
if (_updateScrubberTimer == nil) {
_updateScrubberTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(updateScrubber) userInfo:nil repeats:YES];
}
} else if (_videoPlayer.playbackState == MPMoviePlaybackStatePaused || _videoPlayer.playbackState == MPMoviePlaybackStateStopped) {
[_playPauseButton setBackgroundImage:[UIImage imageNamed:#"video_controls_play.png"] forState:UIControlStateNormal];
_playButtonLarge.hidden = NO;
if (_updateScrubberTimer != nil) {
[_updateScrubberTimer invalidate];
_updateScrubberTimer = nil;
}
if (_videoPlayer.playbackState == MPMoviePlaybackStateStopped) {
_scrubber.value = 0.0f;
_timePlayedLabel.text = #"0:00";
_timeRemainingLabel.text = #"-0:00";
_videoPlayerBG.hidden = NO;
}
}
} else if ([[notification name] isEqualToString:MPMoviePlayerPlaybackDidFinishNotification]) {
NSNumber *reason = [[notification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
if ([reason intValue] == MPMovieFinishReasonPlaybackEnded) {
_videoPlayerBG.hidden = NO;
}
_scrubber.value = _scrubber.maximumValue;
}
}
- (void)playPause {
if ([_videos count] > 0) {
if (_videoPlayer.playbackState == MPMoviePlaybackStatePaused || _videoPlayer.playbackState == MPMoviePlaybackStateStopped) {
_playButtonLarge.hidden = YES;
_videoPlayerBG.hidden = YES;
if ([_videoPlayer contentURL] == nil) {
Video *video = [_videos objectAtIndex:0];
[_videoPlayer setContentURL:video.videoURL];
}
if (![_videoPlayer isPreparedToPlay]) {
[_videoPlayer prepareToPlay];
}
[_videoPlayer play];
} else if (_videoPlayer.playbackState == MPMoviePlaybackStatePlaying) {
_playButtonLarge.hidden = NO;
[_videoPlayer pause];
}
}
}
I think I figured it out, I put the following code before every call to play
if (![_videoPlayer isPreparedToPlay]) {
[_videoPlayer prepareToPlay];
}
works now, if anyone else has any input let me know

AVAudioPlayer fade volume out

I have an AVAudioPlayer playing some audio (duh!)
The audio is initiated when the user presses a button.
When they release it I want the audio to fade out.
I am using Interface builder...so I am trying to hook up a function on "touch up inside" that fades the audio out over 1 sec then stops.
Any ideas?
Thanks
Here's how I'm doing it:
-(void)doVolumeFade
{
if (self.player.volume > 0.1) {
self.player.volume = self.player.volume - 0.1;
[self performSelector:#selector(doVolumeFade) withObject:nil afterDelay:0.1];
} else {
// Stop and get the sound ready for playing again
[self.player stop];
self.player.currentTime = 0;
[self.player prepareToPlay];
self.player.volume = 1.0;
}
}
.
11 years later: do note that setVolume#fadeDuration now exists!
Swift has an AVAudioPlayer method you can use for fading out which was included as of iOS 10.0:
var audioPlayer = AVAudioPlayer()
...
audioPlayer.setVolume(0, fadeDuration: 3)
I tackled this problem using an NSOperation subclass so fading the volume doesn't block the main thread. It also allows fades to be queued and and forgotten about. This is especially useful for playing one shot sounds with fade-in and fade-out effects as they are dealloced after the last fade is completed.
// Example of MXAudioPlayerFadeOperation in NSOperationQueue
NSOperationQueue *audioFaderQueue = [[NSOperationQueue alloc] init];
[audioFaderQueue setMaxConcurrentOperationCount:1]; // Execute fades serially.
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"bg" ofType:#"mp3"]; // path to bg.mp3
AVAudioPlayer *player = [[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:filePath] error:NULL] autorelease];
[player setNumberOfLoops:-1];
[player setVolume:0.0];
// Note that delay is delay after last fade due to the Operation Queue working serially.
MXAudioPlayerFadeOperation *fadeIn = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:player toVolume:1.0 overDuration:3.0];
[fadeIn setDelay:2.0];
MXAudioPlayerFadeOperation *fadeDown = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:player toVolume:0.1 overDuration:3.0];
[fadeDown setDelay:0.0];
MXAudioPlayerFadeOperation *fadeUp = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:player toVolume:1.0 overDuration:4.0];
[fadeUp setDelay:0.0];
MXAudioPlayerFadeOperation *fadeOut = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:player toVolume:0.0 overDuration:3.0];
[fadeOut setDelay:2.0];
[audioFaderQueue addOperation:fadeIn]; // 2.0s - 5.0s
[audioFaderQueue addOperation:fadeDown]; // 5.0s - 8.0s
[audioFaderQueue addOperation:fadeUp]; // 8.0s - 12.0s
[audioFaderQueue addOperation:fadeOut]; // 14.0s - 17.0s
[fadeIn release];
[fadeDown release];
[fadeUp release];
[fadeOut release];
For MXAudioPlayerFadeOperation class code see this post.
I ended up combining some of the answer together and converted it to Swift ending up in this method:
func fadeVolumeAndPause(){
if self.player?.volume > 0.1 {
self.player?.volume = self.player!.volume - 0.1
var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.fadeVolumeAndPause()
})
} else {
self.player?.pause()
self.player?.volume = 1.0
}
}
These are all good answers, however they don't deal with specifying the rate of fade (or applying a logarithmic curve to the fade, which is sometimes desirable), or specifying the number of dB's reduction from unity that you are fading to.
this is an except from one of my apps, with a few "bells and whistles" removed, that are not relevant to this question.
enjoy!
#define linearToDecibels(linear) (MIN(10,MAX(-100,20.0 * log10(linear))))
#define decibelsToLinear(decibels) (pow (10, (0.05 * decibels)))
#define fadeInfoId(n) [fadeInfo objectForKey:##n]
#define fadeInfoObject(NSObject,n) ((NSObject*) fadeInfoId(n))
#define fadeInfoFloat(n) [fadeInfoId(n) floatValue]
#define useFadeInfoObject(n) * n = fadeInfoId(n)
#define useFadeInfoFloat(n) n = fadeInfoFloat(n)
#define setFadeInfoId(n,x) [fadeInfo setObject:x forKey:##n]
#define setFadeInfoFloat(n,x) setFadeInfoId(n,[NSNumber numberWithFloat:x])
#define setFadeInfoFlag(n) setFadeInfoId(n,[NSNumber numberWithBool:YES])
#define saveFadeInfoId(n) setFadeInfoId(n,n)
#define saveFadeInfoFloat(n) setFadeInfoFloat(n,n)
#define fadeAVAudioPlayer_default nil
#define fadeAVAudioPlayer_linearFade #"linearFade"
#define fadeAVAudioPlayer_fadeToStop #"fadeToStop"
#define fadeAVAudioPlayer_linearFadeToStop #"linearFadeToStop"
-(void) fadeAVAudioPlayerTimerEvent:(NSTimer *) timer {
NSMutableDictionary *fadeInfo = timer.userInfo;
NSTimeInterval elapsed = 0 - [fadeInfoObject(NSDate,startTime) timeIntervalSinceNow];
NSTimeInterval useFadeInfoFloat(fadeTime);
float useFadeInfoFloat(fadeToLevel);
AVAudioPlayer useFadeInfoObject(player);
double linear;
if (elapsed>fadeTime) {
if (fadeInfoId(stopPlaybackAtFadeTime)) {
[player stop];
linear = fadeInfoFloat(fadeFromLevel);
} else {
linear = fadeToLevel;
}
[timer invalidate];
[fadeInfo release];
} else {
if (fadeInfoId(linearCurve)) {
float useFadeInfoFloat(fadeFromLevel);
float fadeDelta = fadeToLevel-fadeFromLevel;
linear = fadeFromLevel + (fadeDelta * (elapsed/fadeTime));
} else {
float useFadeInfoFloat(fadeToDB);
float useFadeInfoFloat(fadeFromDB);
float fadeDelta = fadeToDB-fadeFromDB;
float decibels = fadeFromDB + (fadeDelta * (elapsed/fadeTime));
linear = decibelsToLinear(decibels);
}
}
[player setVolume: linear];
//[self displayFaderLevelForMedia:player];
//[self updateMediaVolumeLabel:player];
}
-(void) fadeAVAudioPlayerLinear:(AVAudioPlayer *)player over:(NSTimeInterval) fadeTime fadeToLevel:(float) fadeToLevel fadeMode:(NSString*)fadeMode {
NSMutableDictionary *fadeInfo = [[NSMutableDictionary alloc ]init];
saveFadeInfoId(player);
float fadeFromLevel = player.volume;// to optimize macros put value in var, so we don't call method 3 times.
float fadeFromDB = linearToDecibels(fadeFromLevel);
float fadeToDB = linearToDecibels(fadeToLevel);
saveFadeInfoFloat(fadeFromLevel);
saveFadeInfoFloat(fadeToLevel);
saveFadeInfoFloat(fadeToDB);
saveFadeInfoFloat(fadeFromDB);
saveFadeInfoFloat(fadeTime);
setFadeInfoId(startTime,[NSDate date]);
if([fadeMode isEqualToString:fadeAVAudioPlayer_fadeToStop]||[fadeMode isEqualToString:fadeAVAudioPlayer_linearFadeToStop]){
setFadeInfoFlag(stopPlaybackAtFadeTime);
}
if([fadeMode isEqualToString:fadeAVAudioPlayer_linearFade]||[fadeMode isEqualToString:fadeAVAudioPlayer_linearFadeToStop]){
setFadeInfoFlag(linearCurve);
}
[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(fadeAVAudioPlayerTimerEvent:) userInfo:fadeInfo repeats:YES];
}
-(void) fadeAVAudioPlayer:(AVAudioPlayer *)player over:(NSTimeInterval) fadeTime fadeToDB:(float) fadeToDB fadeMode:(NSString*)fadeMode {
[self fadeAVAudioPlayerLinear:player over:fadeTime fadeToLevel:decibelsToLinear(fadeToDB) fadeMode:fadeMode ];
}
-(void) fadeoutAVAudioPlayer:(AVAudioPlayer *)player {
[self fadeAVAudioPlayerLinear:player over:5.0 fadeToLevel:0 fadeMode:fadeAVAudioPlayer_default];
}
-(void) fadeinAVAudioPlayer:(AVAudioPlayer *)player {
[self fadeAVAudioPlayerLinear:player over:5.0 fadeToLevel:0 fadeMode:fadeAVAudioPlayer_default];
}
An extension for swift 3 inspired from the most voted answer. For those of you who like copy-pasting :)
extension AVAudioPlayer {
func fadeOut() {
if volume > 0.1 {
// Fade
volume -= 0.1
perform(#selector(fadeOut), with: nil, afterDelay: 0.1)
} else {
// Stop and get the sound ready for playing again
stop()
prepareToPlay()
volume = 1
}
}
}
I wrote a helper class in Swift for fading AvAudioPlayer in and out. You can use logarithmic volume function for more gradual fading effect.
let player = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
let fader = iiFaderForAvAudioPlayer(player: player)
fader.fadeIn()
fader.fadeOut()
Here a demo app: https://github.com/evgenyneu/sound-fader-ios
Swift 3
I like Ambroise Collon answer's , so i voted up but Swift is statically typed so the performSelector: methods are to fall by the wayside, maybe an alternative could be dispatch async (in this version I've added also the destination volume as parameter)
func dispatchDelay(delay:Double, closure:#escaping ()->()) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: closure)
}
extension AVAudioPlayer {
func fadeOut(vol:Float) {
if volume > vol {
//print("vol is : \(vol) and volume is: \(volume)")
dispatchDelay(delay: 0.1, closure: {
[weak self] in
guard let strongSelf = self else { return }
strongSelf.volume -= 0.01
strongSelf.fadeOut(vol: vol)
})
} else {
volume = vol
}
}
func fadeIn(vol:Float) {
if volume < vol {
dispatchDelay(delay: 0.1, closure: {
[weak self] in
guard let strongSelf = self else { return }
strongSelf.volume += 0.01
strongSelf.fadeIn(vol: vol)
})
} else {
volume = vol
}
}
}
This seems to me to be a descent use of an NSOperationQueue.
Hence here is my solution:
-(void) fadeIn
{
if (self.currentPlayer.volume >= 1.0f) return;
else {
self.currentPlayer.volume+=0.10;
__weak typeof (self) weakSelf = self;
[NSThread sleepForTimeInterval:0.2f];
[self.fadingQueue addOperationWithBlock:^{
NSLog(#"fading in %.2f", self.currentPlayer.volume);
[weakSelf fadeIn];
}];
}
}
-(void) fadeOut
{
if (self.currentPlayer.volume <= 0.0f) return;
else {
self.currentPlayer.volume -=0.1;
__weak typeof (self) weakSelf = self;
[NSThread sleepForTimeInterval:0.2f];
[self.fadingQueue addOperationWithBlock:^{
NSLog(#"fading out %.2f", self.currentPlayer.volume);
[weakSelf fadeOut];
}];
}
}
How about this: (if time passed in is negative then fade out the sound, otherwise fade in)
- (void) fadeInOutVolumeOverTime: (NSNumber *)time
{
#define fade_out_steps 0.1
float theVolume = player.volume;
NSTimeInterval theTime = [time doubleValue];
int sign = (theTime >= 0) ? 1 : -1;
// before we call this, if we are fading out, we save the volume
// so that we can restore back to that level in the fade in
if ((sign == 1) &&
((theVolume >= savedVolume) ||
(theTime == 0))) {
player.volume = savedVolume;
}
else if ((sign == -1) && (theVolume <= 0)) {
NSLog(#"fading");
[player pause];
[self performSelector:#selector(fadeInOutVolumeOverTime:) withObject:[NSNumber numberWithDouble:0] afterDelay:1.0];
}
else {
theTime *= fade_out_steps;
player.volume = theVolume + fade_out_steps * sign;
[self performSelector:#selector(fadeInOutVolumeOverTime:) withObject:time afterDelay:fabs(theTime)];
}
}
In Objective-C try this:
NSURL *trackURL = [[NSURL alloc]initFileURLWithPath:#"path to audio track"];
// fade duration in seconds
NSTimeInterval fadeDuration = 0.3;
// duration of the audio track in seconds
NSTimeInterval audioTrackDuration = 5.0;
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:trackURL error:nil];
[audioPlayer play];
// first we set the volume to 1 - highest
[audioPlayer setVolume:1.0];
// then to 0 - lowest
[musicAudioPlayer setVolume:0 fadeDuration:audioTrackDuration - fadeDuration];
Swift solution:
The top rated answer here is great but it gives a stuttering effect as the volume step of 0.1 is too much. Using 0.01 gives a smoother fade effect to hear.
Using this code you can specify how long you want the fade transition to last.
let fadeVolumeStep: Float = 0.01
let fadeTime = 0.5 // Fade time in seconds
var fadeVolumeStepTime: Double {
return fadeTime / Double(1.0 / fadeVolumeStep)
}
func fadeOut() {
guard let player = self.player else {
return
}
if !player.playing { return }
func fadeOutPlayer() {
if player.volume > fadeVolumeStep {
player.volume -= fadeVolumeStep
delay(time: fadeVolumeStepTime, closure: {
fadeOutPlayer()
})
} else {
player.stop()
player.currentTime = 0
player.prepareToPlay()
}
}
fadeOutPlayer()
}
func fadeIn() {
guard let player = self.player else {
return
}
if player.playing { return }
player.volume = 0
player.play()
func fadeInPlayer() {
if player.volume <= 1 - fadeVolumeStep {
player.volume += fadeVolumeStep
delay(time: fadeVolumeStepTime, closure: {
fadeInPlayer()
})
} else {
player.volume = 1
}
}
fadeInPlayer()
}
func delay(time delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
you can adjust the time using fadeTime constant.