I'm having a heck of a time figuring this out.
I have a modal view that appears when tapping on one of the tabs of a tab bar. In the new view that takes over the whole screen, there is a button a user taps to start recording audio, and when they're finished recording audio, or if they cancel recording, the modal view is dismissed.
The problem is that, once they start recording, that red recording bar should appear on this new view. But it's not. And as soon as that view is dismissed, in the next view that is presented (a table view, which is found in one of the tabs), you can see the red recording bar at the top for a split second and it disappears BUT it shoves the tab bar down the screen and obscures part of the tab bar, which you can see in the third screenshot below.
Modal view pops up - recording is currently in progress, but red recording indicator is not showing at the top
Right when the recording is done and the view is about to disappear, the red bar appears
And once the view disappears and we're left with the table view that lives in one of the tabs, the tab bar is shoved down past the bottom of the screen :( It SHOULD look like this (with the fourth tab selected):
My questions:
1) What am I doing wrong that's causing the red recording bar to NOT show up in the modal view?
2) Is there a way to maybe refresh this view from the screenshot so that when it appears, it resizes properly?
Here's the code. I removed some of the non-important stuff that doesn't deal with the views.
#interface AudioViewController ()
#end
#implementation AudioViewController
#synthesize fileData;
UILabel *countdownLabel;
- (void)viewDidLoad
{
[super viewDidLoad];
self.friendsRelation = [[PFUser currentUser] objectForKey:#"friendsRelation"];
self.recipients = [[NSMutableArray alloc] init];
}
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.audioPicker = [[UIViewController alloc] init];
self.audioPicker.view.backgroundColor = [UIColor yellowColor];
self.friendsRelation = [[PFUser currentUser] objectForKey:#"friendsRelation"];
PFQuery *query = [self.friendsRelation query];
[query orderByAscending:#"username"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"Error %# %#", error, [error userInfo]);
}
else {
self.friends = objects;
[self.tableView reloadData];
}
}];
UIButton *cancelBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
cancelBtn.frame = CGRectMake(50.0, 200.0, 200.0, 200.0);
cancelBtn.titleLabel.font = [UIFont systemFontOfSize:20];
[cancelBtn setTitle:#"Cancel" forState:UIControlStateNormal];
[self.audioPicker.view addSubview:cancelBtn];
cancelBtn.center = CGPointMake(self.view.center.x, 400);
[cancelBtn addTarget:self action:#selector(exitRecordingScreen) forControlEvents:UIControlEventTouchUpInside];
UIButton *recordBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
recordBtn.frame = CGRectMake(50.0, 50.0, 200.0, 200.0);
recordBtn.titleLabel.font = [UIFont systemFontOfSize:50];
[recordBtn setTitle:#"Record" forState:UIControlStateNormal];
recordBtn.center = CGPointMake(self.view.center.x, 100);
[self.audioPicker.view addSubview:recordBtn];
if ([self respondsToSelector:#selector(timeout)]) {
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timeout) userInfo:nil repeats:NO];
} else {
NSLog(#"Error: missing selector");
}
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord
withOptions:AVAudioSessionCategoryOptionDuckOthers
error:nil];
if (!fileData) {
[self presentViewController:self.audioPicker animated:NO completion:nil];
NSLog(#"File data: %#", fileData);
[recordBtn addTarget:self action:#selector(startRecordingAudio) forControlEvents:UIControlEventTouchUpInside];
} else {
NSLog(#"Existing File data: %#", fileData);
}
}
- (void) timeout {
[self.navigationController popViewControllerAnimated:YES];
}
# pragma mark - Audio Recording Methods
////////
// Removed some stuff here that is not manipulating views
////////
- (void) stopRecordingOnAudioRecorder:(AVAudioRecorder *)paramRecorder{
/* Just stop the audio recorder here */
[paramRecorder stop];
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder
successfully:(BOOL)flag{
if (flag) {
NSLog(#"Stopped recording process");
NSError *playbackError = nil;
NSError *readingError = nil;
fileData = [NSData dataWithContentsOfURL:[self audioRecordingPath]
options:NSDataReadingMapped
error:&readingError];
self.audioPlayer = [[AVAudioPlayer alloc] initWithData:fileData
error:&playbackError];
if (self.audioPlayer != nil) {
self.audioPlayer.delegate = self;
//Prepare and start playing
if ([self.audioPlayer prepareToPlay] && [self.audioPlayer play]) {
NSLog(#"Started playing recorded audio");
} else {
NSLog(#"Couldn't play recorded audio");
}
} else {
NSLog(#"Failed to create audio player");
}
} else {
NSLog(#"Stopping audio recording failed");
}
self.audioRecorder = nil;
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player
successfully:(BOOL)flag{
if (flag){
NSLog(#"Audio player stopped correctly.");
} else {
NSLog(#"Audio player did not stop correctly.");
}
if ([player isEqual:self.audioPlayer]){
self.audioPlayer = nil;
} else {
/* This is not the player */
}
}
# pragma mark - TableView methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.friends count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
PFUser *user = [self.friends objectAtIndex:indexPath.row];
cell.textLabel.text = user.username;
// makes sure checkmark isn't reused if user didn't explicitly select name
if ([self.recipients containsObject:user.objectId]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)reset {
self.audioFile = nil;
}
// User hits "Cancel" button
-(void)exitRecordingScreen {
[self reset];
[self.presentedViewController dismissViewControllerAnimated:NO completion:nil];
[self.tabBarController setSelectedIndex:0];
NSLog(#"exit recording screen button pressed");
}
- (IBAction)send:(id)sender {
if (self.audioFile == nil) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Please try again." message:#"Please record audio again to share." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
[self presentViewController:self.audioPicker animated:NO completion:nil];
} else {
[self uploadMessage];
[self.tabBarController setSelectedIndex:0];
}
}
// Cancel sending recorded file
- (void)cancel:(id)sender {
fileData = nil;
[self reset];
[self.tabBarController setSelectedIndex:0];
}
#end
Sorry for the wall of text and the length. I'm really stumped.
Solution : You have to reset frame for UITabBarController.
1. Initially frame for UITabBarController will be (0,0,screenWidth,screenHeight).
2. But when this recording red bar appear it becomes (0,20,screenWidth,screenHeight)
3. Here you are supposed to change height for UITabBarController
CGRect changedFrame = objMainTabBarController.view.frame;
changedFrame.size.height = [UIScreen mainScreen].bounds.size.height - CGRectGetMinY(changedFrame);
objMainTabBarController.view.frame = changedFrame;
That's it..
Related
I have a UIView that contains a progress bar. What I want to do is simple, I have a button, user clicks that button, app downloads file and show progress in progress bar. I am able to do this when the user clicks the download button the first time. But when the user clicks the second time to download again, NSURLSession delegates are not called.
My UIView .m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self configure];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self configure];
}
return self;
}
-(void)configure
{
[self createSpinner];
[self createProgressBar];
NSArray *URLs = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
self.docDirectoryURL = [URLs objectAtIndex:0];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"com.tinkytickles"];
sessionConfiguration.HTTPMaximumConnectionsPerHost = 1;
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
delegate:self
delegateQueue:nil];
}
-(void)createSpinner
{
[self setBackgroundColor:[UIColor colorWithWhite:1.0f alpha:0.5f]];
spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[self addSubview:spinner];
[spinner setColor:original_new_dark_grey];
[spinner setUserInteractionEnabled:NO];
[spinner setCenter:CGPointMake([[UIScreen mainScreen] bounds].size.width/2, [[UIScreen mainScreen] bounds].size.height/2)];
[spinner setFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
[spinner startAnimating];
}
-(void)createProgressBar
{
self.progressBar = [[TYMProgressBarView alloc] initWithFrame:CGRectMake(0, 0, 280, 15)];
[self.progressBar setBarBackgroundColor:[UIColor whiteColor]];
[self.progressBar setBarBorderColor:original_new_dark_grey];
[self.progressBar setBarFillColor:original_new_dark_grey];
[self.progressBar setBarBorderWidth:1.0f];
[self addSubview:self.progressBar];
[self.progressBar setCenter:CGPointMake([[UIScreen mainScreen] bounds].size.width/2, [[UIScreen mainScreen] bounds].size.height/2)];
[self.progressBar setHidden:YES];
self.label = [[UILabel alloc] initWithFrame:CGRectMake(self.progressBar.frame.origin.x, self.progressBar.frame.origin.y - 30, self.progressBar.frame.size.width, 25)];
[self.label setText:NSLocalizedString(locDownloading, nil)];
[self.label setTextAlignment:NSTextAlignmentCenter];
[self.label setTextColor:original_new_dark_grey];
[self.label setFont:quicksand_14];
[self addSubview:self.label];
[self.label setHidden:YES];
}
-(void)showProgressBarWithProgress:(CGFloat)progress withText:(NSString *)text
{
[spinner setHidden:YES];
[self.label setText:[NSString stringWithFormat:NSLocalizedString(locDownloadingAt, nil), text]];
[self.label setHidden:NO];
[self.progressBar setHidden:NO];
[self.progressBar setProgress:progress];
}
-(void)stopAnimating
{
[spinner stopAnimating];
}
-(void)startDownloadingURL:(PromoterDownloadInfo *)downloadInfo
{
info = downloadInfo;
if (!info.isDownloading)
{
if (info.taskIdentifier == -1)
{
info.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:info.downloadSource]];
info.taskIdentifier = info.downloadTask.taskIdentifier;
[info.downloadTask resume];
}
else
{
info.downloadTask = [self.session downloadTaskWithResumeData:info.taskResumeData];
[info.downloadTask resume];
info.taskIdentifier = info.downloadTask.taskIdentifier;
}
}
else
{
[info.downloadTask cancelByProducingResumeData:^(NSData *resumeData) {
if (resumeData != nil) {
info.taskResumeData = [[NSData alloc] initWithData:resumeData];
}
}];
}
info.isDownloading = !info.isDownloading;
}
-(void)stopDownload:(PromoterDownloadInfo *)downloadInfo
{
if (!info.isDownloading)
{
if (info.taskIdentifier == -1)
{
info.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:info.downloadSource]];
}
else
{
info.downloadTask = [self.session downloadTaskWithResumeData:info.taskResumeData];
}
info.taskIdentifier = info.downloadTask.taskIdentifier;
[info.downloadTask resume];
info.isDownloading = YES;
}
[self stopAnimating];
[self removeFromSuperview];
}
#pragma mark - NSURLSession Delegate method implementation
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *destinationFilename = downloadTask.originalRequest.URL.lastPathComponent;
NSURL *destinationURL = [self.docDirectoryURL URLByAppendingPathComponent:destinationFilename];
if ([fileManager fileExistsAtPath:[destinationURL path]]) {
[fileManager removeItemAtURL:destinationURL error:nil];
}
BOOL success = [fileManager copyItemAtURL:location
toURL:destinationURL
error:&error];
if (success) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self stopAnimating];
[self removeFromSuperview];
}];
}
else
{
NSLog(#"Unable to copy temp file. Error: %#", [error localizedDescription]);
}
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
if (error != nil) {
NSLog(#"Download completed with error: %#", [error localizedDescription]);
}
else{
NSLog(#"Download finished successfully.");
}
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
if (totalBytesExpectedToWrite == NSURLSessionTransferSizeUnknown) {
NSLog(#"Unknown transfer size");
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
info.downloadProgress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
[self showProgressBarWithProgress:info.downloadProgress withText:info.fileTitle];
});
}
}
-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
// Check if all download tasks have been finished.
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([downloadTasks count] == 0) {
if (appDelegate.backgroundTransferCompletionHandler != nil) {
// Copy locally the completion handler.
void(^completionHandler)() = appDelegate.backgroundTransferCompletionHandler;
// Make nil the backgroundTransferCompletionHandler.
appDelegate.backgroundTransferCompletionHandler = nil;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// Call the completion handler to tell the system that there are no other background transfers.
completionHandler();
// Show a local notification when all downloads are over.
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = NSLocalizedString(locDownloadComplete, nil);
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}];
}
}
}];
}
I use this UIView like this:
PromoterDownloadInfo *info = [[PromoterDownloadInfo alloc] initWithFileTitle:self.title andDownloadSource:#"https://www.mywebsite.com/file.zip"];
PromotersDownloadView *downloadView = [[PromotersDownloadView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.navigationController.view addSubview:downloadView];
[downloadView startDownloadingURL:info];
The first time I clicked the download button it works great. The second time NSURLSession only didCompleteWithError method gets called. Here is what I get from log the second time:
2016-05-12 00:50:47.440 APP[32990:1230071] A background URLSession with identifier com.app already exists!
2016-05-12 00:50:50.614 APP[32990:1230386] Download finished successfully.
What am I doing wrong? I tried to create NSURLSessionConfiguration only once but this way no delegate method gets called. What should I do?
You said:
The first time I clicked the download button it works great. ... Here is what I get from log the second time:
2016-05-12 00:50:47.440 APP[32990:1230071] A background URLSession with identifier com.app already exists!<br />
That error is pointing out that you want to instantiate only one background NSURLSession for a given identifier (and you generally only need/want a single background session). If you were going to instantiate multiple ones, you'd give them unique identifiers, but handling background sessions is complicated enough without unnecessarily having multiple sessions. I'd suggest that you only want a single background session.
You said:
I tried to create NSURLSessionConfiguration only once but this way no delegate method gets called.
Yes, you should have one session configuration. And, just as importantly, only one background session object.
I suspect that there's an issue with your delegate object not being able to keep track of which view it should be updating. Or perhaps you lost reference to your session object and your reference was nil. It could be a couple of different things, and it's hard to know without seeing how you did this.
I'd suggest moving this session configuration code out of the view, and have some shared instance that you can reference anywhere (e.g. a singleton works well, so you can instantiate it from wherever it's first needed, whether from a view or from the app delegate's handleEventsForBackgroundURLSession method).
The only challenge then is how to keep track of which views are keeping track of which network requests. Do you want to have a single view that will keep track of all incomplete requests, regardless of when this view is instantiated? If so, you can use NSNotificationCenter notifications (that way, any view that wants to be notified of progress updates can just observe your custom notification). Or does a given view only care about requests that you initiated from that view?In that case, you might maintain dictionary that maps taskIdentifier values to which view or object needs to know about the status updates (what way you can have your session object keep track of which views care about which tasks). It just depends upon your app's requirements.
I am programing an audio player using "AVFoundation.h". I've issues with Updating Progress bar, Hence when I click at play button my app gives error. I am attaching both code samples & error report. can anyone sort this out?
-(void)updateProgress {
NSInteger durationMinutes = [self.audioPlayer duration] / 60;
NSInteger durationSeconds = [self.audioPlayer duration] - durationMinutes * 60;
NSInteger currentTimeMinutes = [self.audioPlayer currentTime] / 60;
NSInteger currentTimeSeconds = [self.audioPlayer currentTime] - currentTimeMinutes * 60;
NSString *progressString = [NSString stringWithFormat:#"%d:%02d / %d:%02d", currentTimeMinutes, currentTimeSeconds, durationMinutes, durationSeconds];
self.timeLabel.text = progressString;
self.progressBar.progress = [self.audioPlayer currentTime] / [self.audioPlayer duration];
NSNumber *numCurrentTimeSeconds = [NSNumber numberWithInt:currentTimeSeconds];
NSNumber *numDurationSeconds = [NSNumber numberWithInt:durationSeconds];
NSString *songTitle = [self.selectedFilePath lastPathComponent];
NSString *artistName = #"MyPlayer";
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:#"placeholder"]];
MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
NSDictionary *infoDict = [NSDictionary dictionaryWithObjects:#[songTitle, artistName, albumArt, numDurationSeconds, numCurrentTimeSeconds] forKeys:#[MPMediaItemPropertyTitle, MPMediaItemPropertyAlbumArtist, MPMediaItemPropertyArtwork, MPMediaItemPropertyPlaybackDuration, MPNowPlayingInfoPropertyElapsedPlaybackTime]];
[infoCenter setNowPlayingInfo:infoDict]; }
When pressed Build & Run app goes successfully started in simulator, I've taken 2 images of active Console
1. before clicking at play button.
After clicking play button. when app goes crash.
enter image description here
now please suggest me what I should do at this point? so that my app start working smoothly...
Thanks
Faiz.
After following instructions by Losiowaty answer last day. those yellow issues are removed but still my programing gives same error when I click at play button.
enter image description here
This time I am uploading complete code and highlighting few things due to which I think error is occurring.
Please take a look at my mainwviewcontroller.m class code.
#interface MainViewController ()
#end
#implementation MainViewController
#synthesize audioPlayer;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
if (error == nil) {
NSLog(#"audio session initialized successfully");
} else {
NSLog(#"error initializing audio session: %#", [error description]);
}
[audioPlayer setDelegate:self];
MPVolumeView *volumeView = [ [MPVolumeView alloc] init] ;
[volumeView setFrame:self.airPlayView.bounds];
[self.airPlayView addSubview:volumeView];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(caughtInterruption:) name:AVAudioSessionInterruptionNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(routeChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(BOOL)canBecomeFirstResponder
{
return YES;
}
-(void)dealloc
{
[self resignFirstResponder];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showFilePicker"]) {
UINavigationController *navigationController = (UINavigationController *)segue.destinationViewController;
FileViewController *fileViewController = (FileViewController *)navigationController.topViewController;
fileViewController.delegate = self;
}
}
#pragma mark - file picker delegate methods
-(void)cancel
{
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)didFinishWithFile:(NSString *)filePath
{
NSError *error = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
self.selectedFilePath = filePath;
NSString *relativeFilePath = [documentsDirectory stringByAppendingPathComponent:filePath];
NSURL *fileURL = [NSURL fileURLWithPath:relativeFilePath];
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];
self.audioPlayer.delegate = self;
if (error == nil) {
NSLog(#"audio player initialized successfully");
self.titleLabel.text = self.selectedFilePath;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateProgress) userInfo:nil repeats:YES];
NSString *songTitle = [filePath lastPathComponent];
NSString *artistName = #"MyPlayer";
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:#"placeholder"]];
MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
NSDictionary *infoDict = [NSDictionary dictionaryWithObjects:#[songTitle, artistName, albumArt] forKeys:#[MPMediaItemPropertyTitle, MPMediaItemPropertyAlbumArtist, MPMediaItemPropertyArtwork]];
[infoCenter setNowPlayingInfo:infoDict];
[self play:nil];
} else {
NSLog(#"error initializing audio player: %#", [error description]);
}
//dismiss the file picker
[self dismissViewControllerAnimated:YES completion:nil];
}
-(IBAction)play:(id)sender
{
if ([self.audioPlayer isPlaying]) {
[self.audioPlayer pause];
[self.playButton setImage:[UIImage imageNamed:#"play"] forState:UIControlStateNormal];
[self.timer invalidate];
[animation stopAnimating];
} else {
[self.audioPlayer play];
[self.playButton setImage:[UIImage imageNamed:#"pause"] forState:UIControlStateNormal];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateProgress) userInfo:nil repeats:YES];
animation.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"animation1.png"],
[UIImage imageNamed:#"animation2.png"],
[UIImage imageNamed:#"animation3.png"],nil];
[animation setAnimationRepeatCount:2000];
animation.animationDuration = 0.5;
[animation startAnimating];
}
self.playbackInterrupted = NO;
}
-(IBAction)skipForward:(id)sender
{
if ([self.audioPlayer isPlaying]) {
NSTimeInterval desiredTime = self.audioPlayer.currentTime + 15.0f;
if (desiredTime < self.audioPlayer.duration) {
self.audioPlayer.currentTime = desiredTime;
}
}
}
-(IBAction)skipBackward:(id)sender
{
if ([self.audioPlayer isPlaying]) {
NSTimeInterval desiredTime = self.audioPlayer.currentTime - 15.0f;
if (desiredTime < 0) {
self.audioPlayer.currentTime = 0.0f;
} else {
self.audioPlayer.currentTime = desiredTime;
}
}
}
#pragma mark - Timer delegate
-(void)updateProgress
{
NSInteger durationMinutes = [self.audioPlayer duration] / 60;
NSInteger durationSeconds = [self.audioPlayer duration] - durationMinutes * 60;
NSInteger currentTimeMinutes = [self.audioPlayer currentTime] / 60;
NSInteger currentTimeSeconds = [self.audioPlayer currentTime] - currentTimeMinutes * 60;
NSString *progressString = [NSString stringWithFormat:#"%ld:%02ld / %ld:%02ld", currentTimeMinutes,currentTimeSeconds, durationMinutes, durationSeconds];
self.timeLabel.text = progressString;
self.progressBar.progress = [self.audioPlayer currentTime] / [self.audioPlayer duration];
NSNumber *numCurrentTimeSeconds = [NSNumber numberWithInteger:currentTimeSeconds];
NSNumber *numDurationSeconds = [NSNumber numberWithInteger:durationSeconds];
NSString *songTitle = [self.selectedFilePath lastPathComponent];
NSString *artistName = #"MyPlayer";
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:#"placeholder"]];
MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
NSDictionary *infoDict = [NSDictionary dictionaryWithObjects:#[songTitle, artistName, albumArt, numDurationSeconds, numCurrentTimeSeconds] forKeys:#[MPMediaItemPropertyTitle, MPMediaItemPropertyAlbumArtist, MPMediaItemPropertyArtwork, MPMediaItemPropertyPlaybackDuration, MPNowPlayingInfoPropertyElapsedPlaybackTime]];
[infoCenter setNowPlayingInfo:infoDict];
}
#pragma mark - AVAudioPlayer delegate methods
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
if (flag) {
[self.playButton setImage:[UIImage imageNamed:#"play"] forState:UIControlStateNormal];
[self.timer invalidate];
[animation stopAnimating];
}
}
#pragma mark - Remote control
-(void)remoteControlReceivedWithEvent:(UIEvent *)event
{
switch (event.subtype) {
case UIEventSubtypeRemoteControlPlay:
case UIEventSubtypeRemoteControlPause:
case UIEventSubtypeRemoteControlTogglePlayPause:
[self play:nil];
break;
case UIEventSubtypeRemoteControlNextTrack:
[self skipForward:nil];
break;
case UIEventSubtypeRemoteControlPreviousTrack:
[self skipBackward:nil];
break;
default:
break;
}
}
#pragma mark - audio interruption
-(void)caughtInterruption:(NSNotification *)notification
{
NSDictionary *userInfo = notification.userInfo;
NSNumber *type =[userInfo objectForKey:AVAudioSessionInterruptionTypeKey];
if ([type integerValue] == AVAudioSessionInterruptionTypeBegan) {
if (self.audioPlayer.playing) {
[self.audioPlayer pause];
[animation stopAnimating];
self.playbackInterrupted = YES;
}
} else {
if (self.audioPlayer.playing == NO && self.playbackInterrupted == YES) {
[self.audioPlayer play];
[animation startAnimating];
self.playbackInterrupted = NO;
}
}
}
#pragma mark - route changed
-(void)routeChanged:(NSNotification *)notification
{
NSDictionary *userInfo = notification.userInfo;
NSNumber *reason =[userInfo objectForKey:AVAudioSessionRouteChangeReasonKey];
switch ([reason integerValue]) {
case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
[self.audioPlayer stop];
[animation stopAnimating];
break;
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
case AVAudioSessionRouteChangeReasonWakeFromSleep:
[self.audioPlayer pause];
[animation stopAnimating];
break;
default:
break;
}
}
#end
above code is error free & pretty clean, everything is clearly mentioned, I am using 4 buttons,
for Play & Pause
for seeking forward
for seeking backward
for entering into document directory for audio file picking
when I am pressing this fourth button it prepares for entering into another view for picking audio file.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showFilePicker"]) {
UINavigationController *navigationController = (UINavigationController *)segue.destinationViewController;
FileViewController *fileViewController = (FileViewController *)navigationController.topViewController;
fileViewController.delegate = self;
}
}
I needs two things to accomplish
the first things which I needs to accomplish is that,
I don't wants to enter into next view, because I am testing my app into simulator where there is no physical audio file I can place or locate in simulator, hence I needs to avoid this thing just for my owns testing purpose.
Hence I am willing to add an audio mp3 file into NSBundle and wants to play this file when I press play button file starts playing & then Pause when pressing again. code for paying & pause is pretty clean working well. but for initializing file path is I think I've to initialize file path in viewDidload method by replacing above view did load by following code.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *audioFilePath = [[NSBundle mainBundle] pathForResource:#"Nameofflie" ofType:#"mp3"];
NSURL *pathAsURL = [[NSURL alloc] initFileURLWithPath:audioFilePath];
NSError *error = nil;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:pathAsURL error:&error];
if (error == nil) {
NSLog(#"audio session initialized successfully");
} else {
NSLog(#"error initializing audio session: %#", [error description]);
}
[audioPlayer setDelegate:self];
MPVolumeView *volumeView = [ [MPVolumeView alloc] init] ;
[volumeView setFrame:self.airPlayView.bounds];
[self.airPlayView addSubview:volumeView];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(caughtInterruption:) name:AVAudioSessionInterruptionNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(routeChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
}
this code also Runs & compiles pretty well but when pressing play button same error occurring. so please suggest me where to place following lines to play MP3 Music file placed from NSBudle .
NSString *audioFilePath = [[NSBundle mainBundle] pathForResource:#"rabne" ofType:#"mp3"];
NSURL *pathAsURL = [[NSURL alloc] initFileURLWithPath:audioFilePath];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:pathAsURL error:&error];
this point is totally different from just above point number 1 which I am willing to accomplish. when after testing successfully, playing that NSBundle audio MP3 file.
I wants to work with my earlier code again, as the end user is not using my simulator, hence for end users I wants to have same option which already I accomplished in above Mainviewcontroller.m class,i.e. user have to press 4th number button for accessing their device's document directory file path. that works pretty well in my code. but the thing here I wants to point to or needs to sort out is that, if someone directly pressing very 1st play button without pressing 4th button for going & selecting an audio file, an alert view should appear with a message that first select a file by pressing fourth button and then click play button. That's it I am willing to have in my code.
Based on the exception in screenshot #2, it looks that you are trying to insert a nil object into an array. The only place where you insert some objects into array is this line :
NSDictionary *infoDict = [NSDictionary dictionaryWithObjects:#[songTitle, artistName, albumArt, numDurationSeconds, numCurrentTimeSeconds] forKeys:#[MPMediaItemPropertyTitle, MPMediaItemPropertyAlbumArtist, MPMediaItemPropertyArtwork, MPMediaItemPropertyPlaybackDuration, MPNowPlayingInfoPropertyElapsedPlaybackTime]];
The second array, the one with the keys, looks ok, as it consisits of only system provided consts. The first one on the other hand has two objects than could be nil : songTitle and albumArt.
The reasons for these to be nil are :
songTitle may be nil if self.selectedFilePath is nil
albumArt - I'm not entirely sure, but it could end up being nil if your image was not found.
Please make sure that these two are not nil and everything should be working fine.
As to your warnings, these two :
NSNumber *numCurrentTimeSeconds = [NSNumber numberWithInt:currentTimeSeconds];
NSNumber *numDurationSeconds = [NSNumber numberWithInt:durationSeconds];
can be fixed by changing to [NSNumber numberWithInteger:] and are caused by the fact that NSInteger is a typedef for long and not int.
The warning on
NSString *progressString = [NSString stringWithFormat:#"%d:%02d / %d:%02d", currentTimeMinutes, currentTimeSeconds, durationMinutes, durationSeconds];
is caused by virtually the same thing. %d expects an int, and NSInteger is a long. Changing %d to %ld will fix it.
It is still my firm belief that these didn't cause the crash, especially based on the thrown exception which is pretty straightforward in stating what has happend.
The provided code confirms my assumptions - the crash happens because self.selectedFilePath is nil in updateProgress method resulting in songTitle also being nil. Why this happens? The only place in provided code where you set self.selectedFilePath is in didFinishWithFile: method, which I assume is a delegate method of FileViewController. If you don't present it and selected something there, that method is not called.
Now, if you want to setup this for testing, the easiest way would be to add this in your viewDidLoad :
NSError *error;
NSString *audioFilePath = [[NSBundle mainBundle] pathForResource:#"rabne" ofType:#"mp3"];
NSURL *pathAsURL = [[NSURL alloc] initFileURLWithPath:audioFilePath];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:pathAsURL error:&error];
if (error == nil) {
self.selectedFilePath = #"test file"; // <<-- IMPORTANT
self.titleLabel.text = self.selectedFilePath;
} else {
NSLog(#"error initializing audio player: %#", [error description]);
}
just above [audioPlayer setDelegate:self];. This should get everything going.
On a side note : I'd also remove this line self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateProgress) userInfo:nil repeats:YES]; from didFinishWithFile: method - you also setup a timer in play: method, and it seems safer to do it once.
As to point #2 - I can give you a hint, that you know if a file is selected when self.selectedFilePath != nil and to take a look at UIAlertViewController class. The rest of work is left for you, as it was not a part of the original problem and has nothing to do with solving the crash. Also, you wouldn't learn anything that way :)
I am working on Homekit iOS app. I have a question that I have an accessory and When I change its power characteristic value using the HomeKit Simulator the delegates of HMAccessory are caliing but in case If I change the powr characteristic value programmatically (Using the writevalue ) the delegate methods are not being called. Please let me know any ideas of suggestions.
Code
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
selectedDevice.delegate = self;
}
HMAccessoryDelegate
- (void)accessory:(HMAccessory *)accessory service:(HMService *)service didUpdateValueForCharacteristic:(HMCharacteristic *)characteristic;
{
NSLog(#"changed");
}
Write Function
UISwitch *sw = [[UISwitch alloc] initWithFrame:CGRectMake(230, 5, 51, 31)];
[cell addSubview:sw];
sw.on = YES;
[sw addTarget:self action:#selector(updateState:) forControlEvents:UIControlEventValueChanged];
-(void)updateState:(UISwitch*)sender
{
HMCharacteristic *characteristic = self.selectedService.characteristics[tag];
[characteristic enableNotification:YES completionHandler:^(NSError *error)
{
if(!error)
{
}
}];
if([characteristic.characteristicType isEqualToString:HMCharacteristicTypePowerState])
{
id val = characteristic.value;
NSString *str = [NSString stringWithFormat:#"%#",val];
if([str isEqualToString:#"0"])
{
id a = characteristic.value;
BOOL b = [a boolValue];
NSNumber *c = [NSNumber numberWithBool:!b];
AppDelegate *appDel = [[UIApplication sharedApplication] delegate];
[characteristic writeValue:c completionHandler:^(NSError *error) {
if (error) {
UIAlertView *alertController = [[UIAlertView alloc] initWithTitle:#"Error" message:[appDel handleErrorCodes:error.code] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertController show];
return;
}
else
{
[serviceCharacteristicsTableView reloadData];
}
}];
}
}
Please let me know if I am not clear
The documentation says that the delegate method is not called when you set the value programatically:
This method is called as a result of a change in value initiated by
the accessory. Programmatic changes initiated by the app do not result
in this method being called.
If you want to do something after writing the characteristic's value succeeded (or failed), you can do it in the completionHandler: block of writeValue:completionHandler: method.
So I present an EKEventViewController with a UINavigationController. From inside the EKEventViewController I am able to edit the event. It presents an EKEventEditViewController. Everything thing works great(cancel/done buttons) except when I delete the event inside the EKEventEditViewController I recieve this
attempt to dismiss modal view controller whose view does not currently appear. self = modalViewController =
Here is my code...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[CalendarViewController connectExchange];
if (connectionEx == YES)
{
NSDate *dateRepresentingThisDay = [self.sortedDays objectAtIndex:indexPath.section];
NSArray *eventsOnThisDay = [self.sections objectForKey:dateRepresentingThisDay];
EKEventViewController *eventViewController = [[EKEventViewController alloc] init];
eventViewController.allowsEditing = YES;
eventViewController.delegate = self;
EKEvent *event = [eventsOnThisDay objectAtIndex:indexPath.row];
eventViewController.event = event;
UINavigationController *navBar = [[UINavigationController alloc]initWithRootViewController:eventViewController];
[self.navigationController presentViewController:navBar animated:YES completion:nil];
}
}
- (void)eventViewController:(EKEventViewController *)controller didCompleteWithAction:(EKEventViewAction)action
{
EKEvent *event = controller.event;
CalendarViewController * __weak weakSelf = self;
// Dismiss the modal view controller
[self dismissViewControllerAnimated:YES completion:^
{
if (action == EKEventViewActionDone)
{
dispatch_async(dispatch_get_main_queue(), ^{
NSError *err;
[self.eventStore saveEvent:event span:EKSpanThisEvent error:&err];
[self updateEvent:event];
});
}
if (action == EKEventViewActionDeleted)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self deleteEvent:event];
NSError *error;
EKEvent *eventRemove = [self.eventStore eventWithIdentifier:event.eventIdentifier];
[self.eventStore removeEvent:eventRemove span:EKSpanThisEvent error:&error];
});
}
if (action == EKEventViewActionResponded)
{
dispatch_async(dispatch_get_main_queue(), ^{
});
}
weakSelf.eventsList = [self fetchEvents];
[weakSelf.tableView reloadData];
NSLog(#"Event Updated");
}];
}
How am I suppose to properly dismiss the ViewControllers after deleting the event via the EKEventEditViewController?
I solved my problem by subclassing EKEventViewController and setting up EKEventEditViewDelegate and EKEventViewDelegate.
Here is my changed didSelectRow(CalendarViewController.m),
EditViewController *eventViewController = [[EditViewController alloc] init];
eventViewController.allowsEditing = YES;
eventViewController.delegate = self;
EKEvent *event = [eventsOnThisDay objectAtIndex:indexPath.row];
eventViewController.event = event;
[self.navigationController pushViewController:eventViewController animated:YES];
self.editingEvent = eventViewController.event;
The EditViewController is my subclass of EKEventViewController.
In the subclass(EditViewController.m) I added my own edit button with a selector in viewDidLoad,
- (void)viewDidLoad
{
[super viewDidLoad];
UIBarButtonItem *editItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self.delegate action:#selector(editCalEvent)];
self.navigationItem.rightBarButtonItem = editItem;
}
Now back in CalendarViewController.m after adding the EKEventEditViewDelegate and EKEventViewDelegate to the #interface I added new methods
- (void)editCalEvent
{
EKEventEditViewController *editController = [[EKEventEditViewController alloc] init];
editController.event = self.editingEvent;
editController.eventStore = self.eventStore;
editController.editViewDelegate = self;
[self presentViewController:editController animated:YES completion:nil];
}
- (void)eventEditViewController:(EKEventEditViewController *)controller didCompleteWithAction (EKEventEditViewAction)action
{
EKEvent *thisEvent = controller.event;
[self dismissViewControllerAnimated:NO completion:^
{
switch (action)
{
{case EKEventEditViewActionCanceled:
//NSLog(#"Canceled action");
break;}
{case EKEventEditViewActionSaved:
[self.eventStore saveEvent:thisEvent span:EKSpanThisEvent error:nil];
[self updateEvent:thisEvent];
break;}
{case EKEventEditViewActionDeleted:
[self deleteEvent:thisEvent];
NSError *error;
EKEvent *eventRemove = [self.eventStore eventWithIdentifier:thisEvent.eventIdentifier];
[self.eventStore removeEvent:eventRemove span:EKSpanThisEvent error:&error];
//NSLog(#"Deleted action");
break;}
{default:
break;}
}
}];
}
- (void)eventViewController:(EKEventViewController *)controller didCompleteWithAction:(EKEventViewAction)action
{
}
Use the editViewDelegate instead of delegate.
I am working in AVAudioplayer in iOS. I am displaying an activity indicator for loading time if a user clicks the play button. My problem is that when I click the play button the loading time activity indicator is not displayed. In playing time activity indicator displayed that is my problem.
-(void)viewDidLoad
{
// loading View
loadingView=[[UILabel alloc]initWithFrame:CGRectMake(135, 200, 40, 40)];
loadingView.backgroundColor=[UIColor whiteColor];
loadingView.clipsToBounds=YES;
loadingView.layer.cornerRadius=10.0;
activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
activityView.frame = CGRectMake(10, 11, activityView.bounds.size.width, activityView.bounds.size.height);
[loadingView addSubview:activityView];
}
-(void)playOrPauseButtonPressed:(id)sender
{
if(playing==NO)
{
[UIApplication sharedApplication].networkActivityIndicatorVisible=YES;
[self.view addSubview:loadingView];
[activityView startAnimating];
[playButton setBackgroundImage:[UIImage imageNamed:#"Pause.png"] forState:UIControlStateNormal];
// Here Pause.png is a image showing Pause Button.
NSError *err=nil;
AVAudioSession *audioSession=[AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
NSLog(#"%# %d",urlsArray,selectedIndex);
NSString *sourcePath=[urlsArray objectAtIndex:selectedIndex];
NSData *objectData=[NSData dataWithContentsOfURL:[NSURL URLWithString:sourcePath]];
audioPlayer = [[AVAudioPlayer alloc] initWithData:objectData error:&err];
if(err)
{
NSLog(#"Error %ld,%#",(long)err.code,err.localizedDescription);
}
NSTimeInterval bufferDuration=0.005;
[audioSession setPreferredIOBufferDuration:bufferDuration error:&err];
if(err)
{
NSLog(#"Error %ld, %#", (long)err.code, err.localizedDescription);
}
double sampleRate = 44100.0;
[audioSession setPreferredSampleRate:sampleRate error:&err];
if(err)
{
NSLog(#"Error %ld, %#",(long)err.code,err.localizedDescription);
}
[audioSession setActive:YES error:&err];
if(err)
{
NSLog(#"Error %ld,%#", (long)err.code, err.localizedDescription);
}
sampRate=audioSession.sampleRate;
bufferDuration=audioSession.IOBufferDuration;
NSLog(#"SampeRate:%0.0fHZI/OBufferDuration:%f",sampleRate,bufferDuration);
audioPlayer.numberOfLoops = 0;
[audioPlayer prepareToPlay];
audioPlayer.delegate=self;
if(!audioPlayer.playing)
{
[audioPlayer play];
}
playing=YES;
}
else if (playing==YES)
{
[playButton setBackgroundImage:[UIImage imageNamed:#"play.png"] forState:UIControlStateNormal];
[audioPlayer pause];
playing=NO;
}
if (self.audioPlayer)
{
[self updateViewForPlayerInfo];
[self updateViewForPlayerState];
[self.audioPlayer setDelegate:self];
}
}
First know the concept on activity indicator before start to implement. Thats good attitude for us.
http://www.ioscreator.com/tutorials/display-an-activity-indicator
use the activity indicator to display a loading process connect the activity indicator to IBOutlet and synthesize in h file and code like this
declare like this in m file
#interface updatepoliticalViewController : UIViewController
{
UIActivityIndicatorView *spinner;
}
and synthesize spinner into ur h file this difenlty work
-(void)temp
{
spinner = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinner.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
[spinner setCenter:CGPointMake(160, 240)];
[self.view addSubview:spinner];
spinner.color = [UIColor blueColor];
spinner.backgroundColor = [UIColor colorWithWhite:0.2 alpha:0.4];
[spinner startAnimating];
[spinner release];
}
use this method to stop the activity
-(void) myMethod{
[spinner stopAnimating];
spinner.hidden = YES;
}
call this function in view didload and specify how long u want to display the loading process
- (void)viewDidLoad
{
[super viewDidLoad];
[self temp];
[self performSelector:#selector(myMethod) withObject:nil afterDelay:5.0f];
}
hope it helps for you