Stop NSTimer when leaving view - ios

I am working on something that will take data from a website every time the counter counts down. The issue is that I want the counter to stop when the user leaves the view and not keep retrieving data. I have been trying to add this in,
[timername invalidate];
to no avail. Can someone tell me what I need to do so the timer stops when the view is left and re-starts from new when someone goes back to the view?
Thanks.
#import "Price.h"
#interface Price ()
#property (strong, nonatomic) IBOutlet UILabel *priceLabel;
#property (strong, nonatomic) IBOutlet UILabel *countdownLabel;
#end
#implementation Price
int counter;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
counter = 10;
[NSTimer scheduledTimerWithTimeInterval:1.0 target:(self) selector:#selector(countdown) userInfo:nil repeats:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)getPrice{
NSURL *url = [NSURL URLWithString:#"myURL"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if (error ==nil){
NSDictionary *spotPrice = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]
;
self.priceLabel.text = [[[spotPrice objectForKey:#"amount"] stringByAppendingString:#" "]stringByAppendingString:[spotPrice objectForKey:#"currency"]];
}
}];
}
-(void)countdown{
counter--;
self.countdownLabel.text = [NSString stringWithFormat:#"%d", counter];
if (counter == 0){
counter = 15;
[self getPrice];

For do it, You need to set #property of your NSTimer and write
[_timername invalidate];
_timername = nil;
to viewWillDisappear: method.
Such like,,
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[_timername invalidate];
_timername = nil;
}

-(void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
dispatch_async(dispatch_get_main_queue(), ^{
if ([timername isValid]) {
[timername invalidate];
timername = nil;
}
});
}

Related

Data from API call not filling inside of method

I decided to go with an embedded API call within a view controller, and I'm having trouble with the data reaching out before the API returns with the information.
How do I wait for the data to be returned before the view controller displays all the values as nulls?
Thanks for any help.
#import "BDChangeApproveController.h"
#import "BDItemChangeDetailAPI.h"
#interface BDChangeApproveController () <NSURLSessionDelegate>
#property (nonatomic, strong) NSURLSession *session;
#property (nonatomic, copy) NSArray *APIItem;
#end
#implementation BDChangeApproveController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)fetchFeedAPIChangeDetail
{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:config
delegate:nil
delegateQueue:nil];
NSString *requestString = #"http://URL.com";
NSURL *url = [NSURL URLWithString:requestString];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask =
[self.session dataTaskWithRequest:req
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error){
NSDictionary *jsonObject1 = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
//NSLog(#"%#", jsonObject1);
self.APIItem = jsonObject1[#"CoDetail"];
NSLog(#"%#", self.APIItem);
}];
[dataTask resume];
}
//API authentication
- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
NSURLCredential *cred =
[NSURLCredential credentialWithUser:#"demouser"
password:#"secret"
persistence:NSURLCredentialPersistenceForSession];
completionHandler(NSURLSessionAuthChallengeUseCredential, cred);
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self fetchFeedAPIChangeDetail];
self.title = #"Action";
self.coNumberLabel.text = self.itemAPI.changeOrder;
//self.recipeImageView.image = [UIImage imageNamed:self.recipe.image];
NSLog(#"testtttt");
NSMutableString *coDetailsText = [NSMutableString string];
coDetailsText =
[[NSMutableString alloc] initWithFormat:#"Review Change Order details bellow\n====================\n%# \n================== \nPlanned Start %#\n==================\nSubcategory: %#\n==================\nService: %#\n==================\nAssociated CIs: %#\n==================\nEnvironment CI: %#\n==================\nApproval Group: %#\n==================\nInitiator : %#\n==================\nCoordinator : %#\n==================\nRisk Level : %#\n==================\nPerforming Group : %#\n==================\nImplementation Plan : %#\n==================\nStatus : %#\n==================\nRecovery Plan : %#\n==================\n",
/*
self.item.title,
self.item.changeOrder,
self.item.subcategory,
self.item.assignmentGroup,
self.item.changeOrder,
self.item.subcategory,
self.item.assignmentGroup,
self.item.approverEid,
self.item.approverEid,
self.item.subcategory,
self.item.assignmentGroup,
self.item.title,
self.item.title,
self.item.title
*/
self.itemAPI.title,
self.itemAPI.plannedStart,
self.itemAPI.subcategory,
self.itemAPI.service,
self.itemAPI.associatedCi,
self.itemAPI.environment,
self.itemAPI.assignmentGroup,
self.itemAPI.initiator,
self.itemAPI.coordinator,
self.itemAPI.riskLevel,
self.itemAPI.performingGroup,
self.itemAPI.implementationPlan,
self.itemAPI.status,
self.itemAPI.recoveryScope
// self.item.valueInDollars,
// self.item.dateCreated,
// self.item.subcategory,
// self.item.service,
// self.item.associatedCIs,
// self.item.environment,
// self.item.approvalGroup,
// self.item.initiator,
// self.item.coordinator,
// self.item.riskLevel,
// self.item.performingGroup,
// self.item.implementationPlan,
// self.item.validationPlan,
//self.item.recoveryScope
];
self.coDetailsTextView.text = coDetailsText;
NSLog(#"overrrr");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
// Adding File
#import <Foundation/Foundation.h>
#import "BDItemChangeDetailAPI.h"
#interface BDChangeApproveController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *coNumberLabel;
#property (weak, nonatomic) IBOutlet UITextView *coDetailsTextView;
#property (nonatomic, strong) BDItemChangeDetailAPI *itemAPI;
#end
Looks like you're doing something asynchronously and expecting it to act synchronously.
Look at the below piece of code you're using in viewDidLoad:
[self fetchFeedAPIChangeDetail]; // this takes some time to complete
self.title = #"Action"; // this happens immediately after the above starts, not ends
self.coNumberLabel.text = self.itemAPI.changeOrder; // so does this, so at this point itemAPI is probably nil
fetchFeedAPICheckDetail is an asynchronous process, so it might take a few seconds to complete, whereas setting the title and coNumberLabel happens immediately after, so you don't yet have the itemAPI information from the URL Session. Your code doesn't wait for fetchFeedAPIChangeDetail to be done with the request before continuing onto the next line.
Declare a function
-(void)refreshTextView
{
// set your text view text with the api result here
self.coNumberLabel.text = self.itemAPI.changeOrder;
}
Call this method in at the end of your request block after NSLog(#"%#", self.APIItem);
Like this
[self performSelectorOnMainThread:#selector(refreshTextView) withObject:nil waitUntilDone:NO];
When you will call this method in the block it will be called and will update the textview text with the api results. The block is asynchronous and so when you update your textview later it doesn't gets updated because the block has not yet completed its function and the array is still nil but calling this method in the block would assure that the result gets assigned and updated in the textview.
UPDATE
and you do have a typo here
#property (nonatomic, copy) NSArray *APIItem;
you are naming it with the class name thats a conflict.
Name it something else like #property (nonatomic, copy) NSArray *apiItem; Actually u have a class name APIItem declared because of which a conflict occurs and that is the very reason it is marked blue in xcode. look all class names are marked blue in your code. Like NSArray , NSURLSession etc –

Authentication error - Integration SkyDrive in iOS application

I am trying to integrate SkyDrive in my iOS application and the following is the code where I have written the login authentication code. The client ID provided here is the one assigned for the application at the developer site. But the authentication always fails. Any idea what is the error ?
static NSString * const CLIENT_ID = #"00000000XXXXXXXXX";
#implementation PSMainViewController
#synthesize appLogo;
#synthesize userInfoLabel;
#synthesize signInButton;
#synthesize viewPhotosButton;
#synthesize userImage;
#synthesize liveClient;
#synthesize currentModal;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
_scopes = [NSArray arrayWithObjects:
#"wl.signin",
#"wl.basic",
#"wl.skydrive",
#"wl.offline_access", nil];
liveClient = [[LiveConnectClient alloc] initWithClientId:CLIENT_ID
scopes:_scopes
delegate:self];
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.appLogo.image = [UIImage imageNamed:#"skydrive.jpeg"];
[self updateUI];
}
- (void)viewDidUnload
{
[self setAppLogo:nil];
[self setUserInfoLabel:nil];
[self setUserImage:nil];
[self setSignInButton:nil];
[self setViewPhotosButton:nil];
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (IBAction)signinButtonClicked:(id)sender {
if (self.liveClient.session == nil)
{
[self.liveClient login:self
scopes:_scopes
delegate:self];
}
else
{
[self.liveClient logoutWithDelegate:self
userState:#"logout"];
}
}
- (IBAction)viewPhotoButtonClicked:(id)sender {
// Create a Navigation controller
PSSkyPhotoViewer *aPhotoViewer = [[PSSkyPhotoViewer alloc] initWithNibName:#"PSSkyPhotoViewer" bundle:nil];
aPhotoViewer.parentVC = self;
self.currentModal = [[UINavigationController alloc] initWithRootViewController:aPhotoViewer];
[self presentModalViewController:self.currentModal animated:YES];
}
- (void) modalCompleted:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
self.currentModal = nil;
}
#pragma mark LiveAuthDelegate
- (void) updateUI {
LiveConnectSession *session = self.liveClient.session;
if (session == nil) {
[self.signInButton setTitle:#"Sign in" forState:UIControlStateNormal];
self.viewPhotosButton.hidden = YES;
self.userInfoLabel.text = #"Sign in with a Microsoft account before you can view your SkyDrive photos.";
self.userImage.image = nil;
}
else {
[self.signInButton setTitle:#"Sign out" forState:UIControlStateNormal];
self.viewPhotosButton.hidden = NO;
self.userInfoLabel.text = #"";
[self.liveClient getWithPath:#"me" delegate:self userState:#"me"];
[self.liveClient getWithPath:#"me/picture" delegate:self userState:#"me-picture"];
}
}
- (void) authCompleted: (LiveConnectSessionStatus) status
session: (LiveConnectSession *) session
userState: (id) userState {
[self updateUI];
}
- (void) authFailed: (NSError *) error
userState: (id)userState {
// Handle error here
}
#pragma mark LiveOperationDelegate
- (void) liveOperationSucceeded:(LiveOperation *)operation {
if ([operation.userState isEqual:#"me"]) {
NSDictionary *result = operation.result;
id name = [result objectForKey:#"name"];
self.userInfoLabel.text = (name != nil)? name : #"";
}
if ([operation.userState isEqual:#"me-picture"]) {
NSString *location = [operation.result objectForKey:#"location"];
if (location) {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:location]];
self.userImage.image = [UIImage imageWithData:data];
}
}
}
- (void) liveOperationFailed:(NSError *)error operation:(LiveOperation *)operation
{
// Handle error here.
}
#end
May be you are not providing the redirect url in case of a desktop application.But if it's the mobile application the you have to check the "Mobile or Desktop client app: " as yes in the api settings

Calling a block from viewdidload

I'm trying to load some JSON data from my model.
But i want to use a block so i can check if there is data with complete YES or NO.
But i call my block from my viewdidload set a breakpoint to check if its called and it comes tot the block but than skips everything between the block.
BlaVIewController.m
#import "MKDataSource.h"
#interface BlaViewController ()
#property (strong, nonatomic) MKDataSource *dataSource;
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#end
#implementation BlaViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.dataSource = [[MKDataSource alloc] init];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.dataSource loadData:^(BOOL complete, NSError *error) {
if (complete) {
//[self.tableView reloadData];
NSLog(#"There is data");
} else {
// error
NSLog(#"No data found");
}
}];
// Do any additional setup after loading the view.
}
And my MKDataSource.m
#interface MKDataSource ()
#property (strong, nonatomic) NSDictionary *data;
#end
#implementation MKDataSource
- (void)loadData:(bool_complete)complete {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
self.data = [NSJSONSerialization JSONObjectWithData:response options:0 error:nil];
NSLog(#"JSON Data %#", self.data);
complete(YES, nil);
}
Please explain what i'm doing wrong. I check the JSON and it works. (i removed the JSON url and replaced it with google.com. To protect my data)
Moved the self.dataSource = [[MKDataSource alloc] init]; from initwithnibname to viewdidload.
WORKS

Getting UITableView to populate with data from another class

I am quite new to Objective-C and this is the first time I have attempted to implement MVC. I have a model class where l have an NSArray which will be populated with data from a JSON object. I want to populate my UITableView (in my view controller class), with objects from this array.
Please review my code:
Droplets.h
#interface Droplets : NSObject {
NSArray *dropletsArray;
}
// Get droplets data
- (void) getDropletsList;
//Object initilization
- (id) init;
//Public properties
#property (strong, nonatomic) NSArray *dropletsArray; // Used to store the selected JSON data objects
#end
Droplets.m
#define kBgQueue dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
#define kDigialOceanApiURL [NSURL URLWithString:#"http://inspiredwd.com/api-test.php"] //Droplets API call
#import "Droplets.h"
#interface Droplets ()
//Private Properties
#property (strong, nonatomic) NSMutableData *data; // Used to store all JSON data objects
#end
#implementation Droplets;
#synthesize dropletsArray;
#synthesize data;
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
- (void) getDropletsList {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSURL *url = kDigialOceanApiURL; // Predefined Digital Ocean URL API http request
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self]; //Should be: [[NSURLConnection alloc]initiWithRequest:request delegate:self]; ...however the instance of NSURLConnection is never used, which results in an "entity unsed" error.
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
data = [[NSMutableData alloc]init]; // mutable data dictionary is allocated and initilized
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData {
[data appendData:theData]; // append 'theData' to the mutable data dictionary
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
//JSON foundation object returns JSON data from a foundation object. Assigned returned data to a dictionary 'json'.
NSDictionary* jsonData = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions error:0];
self.dropletsArray = [jsonData objectForKey:#"droplets"]; //dictionary of arrays
NSLog(#"Droplets %#", self.dropletsArray);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// If the application is unable to connect to The Digital Ocean Server, then display an UIAlertView
UIAlertView *errorView = [[UIAlertView alloc]initWithTitle:#"Error" message:#"Unable to connect to The Digital Ocean Server, please ensure that you are connected via either WIFI or 3G." delegate:nil cancelButtonTitle:#"Dismiss" otherButtonTitles:nil];
[errorView show];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO; // Turn of the network activity indicator
}
#end
DropletsList.h
#class Droplets;
#interface DropletsList : UITableViewController
- (Droplets *) modelDroplets;
#end
DropletsList.m
#define RGB(r, g, b) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1]
#interface DropletsList ()
//Private properties
#property (strong, nonatomic) Droplets *modelDroplets;
#property (strong, nonatomic) NSArray *tableData;
#end
#implementation DropletsList
#synthesize tableData;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
NSLog(#"get my data from model");
}
return self;
}
- (Droplets *) modelDroplets
{
if (!_modelDroplets) _modelDroplets = [[Droplets alloc]init];
return _modelDroplets;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_modelDroplets = [[Droplets alloc]init];
self.tableData = [_modelDroplets dropletsArray];
[_modelDroplets getDropletsList];
[self.tableView reloadData]; // reload the droplets table controller
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView {
return 1; // Return the number of sections.
}
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
return [_modelDroplets.dropletsArray count]; // Return the number of rows in the section.
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// The cell identified by "dropletsList", is assiged as the UITableViewCell
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:#"dropletsList"];
//NSLog(#"Droplets Name: %#",self.dropletsArray);
// The UITableView text label is assigned the contents from 'dropletsArray', with the object key "name"- name of the droplet
cell.textLabel.text=[[tableData objectAtIndex:indexPath.row]objectForKey:#"name"];
// The UITableView text detail label is assigned the contents from 'dropletsArray', with the object key "status"- status of the droplet
cell.detailTextLabel.text=[[tableData objectAtIndex:indexPath.row]objectForKey:#"status"];
//Evalulate the status of each droplet, setting the colour appropriate to the staus
if ([[[tableData objectAtIndex:indexPath.row] objectForKey:#"status"] isEqualToString:#"active"]) {
//Set the detail text label colour
cell.detailTextLabel.textColor = RGB (35,179,0);
}
return cell;
}
#end
Basically my table doesn't populate. Please could someone help?
- (void)viewDidLoad
{
[super viewDidLoad];
_modelDroplets = [[Droplets alloc]init];
self.tableData = [_modelDroplets dropletsArray];
[_modelDroplets getDropletsList];
[self.tableView reloadData]; // reload the droplets table controller
}
In this method you are fetching droplets from a webservice. It is asynchronous, by the time tableView reloads the data it might not have completed fetching the data. You need to have a callback which will reload the tableView on completion of webservice.
EDIT :
Create a class method in Droplets to fetch all data
//Droplets.h
typedef void (^NSArrayBlock)(NSArray * array);
typedef void (^NSErrorBlock)(NSError * error);
//Droplets.m
+ (void)getDropletsWithCompletion:(NSArrayBlock)arrayBlock onError:(NSErrorBlock)errorBlock
{
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:kDigialOceanApiURL];
[urlRequest setHTTPMethod:#"GET"];
[urlRequest setCachePolicy:NSURLCacheStorageNotAllowed];
[urlRequest setTimeoutInterval:30.0f];
[urlRequest addValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[NSURLConnection sendAsynchronousRequest:urlRequest
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *responseData, NSError *error) {
if (error) {
errorBlock(error);
}else{
NSError *serializationError = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONReadingAllowFragments
error:&serializationError];
arrayBlock(json[#"droplets"]);
}
}];
}
//DropletsList.h
- (void)viewDidLoad
{
[super viewDidLoad];
[Droplets getDropletsWithCompletion:^(NSArray *array) {
self.modelDroplets = droplets;
[self.tableView reloadData];
} onError:^(NSError *error) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Error" message:error.localizedDescription delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert show];
}];
}
Disclaimer : Tested and verified :)

Background audio wont play

I'm struggling for couple of days now to sort this thing out and simply just can't find the way. I want to play audio in background when app exits or when i click on link to go to Safari, but i just wont go to background mode. Please help.
FirstViewController.h file :
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVAudioPlayer.h>
#import <AVFoundation/AVFoundation.h>
#interface RygestopFirstViewController : UIViewController <AVAudioPlayerDelegate> {
IBOutlet UIButton *playButton;
IBOutlet UISlider *volumeSlider;
IBOutlet UISlider *progressBar;
IBOutlet UILabel *currentTime;
IBOutlet UILabel *duration;
AVAudioPlayer *player;
UIImage *playBtnBG;
UIImage *pauseBtnBG;
NSTimer *updateTimer;
BOOL inBackground;
}
- (IBAction)playButtonPressed:(UIButton*)sender;
- (IBAction)volumeSliderMoved:(UISlider*)sender;
- (IBAction)progressSliderMoved:(UISlider*)sender;
#property (nonatomic, retain) UIButton *playButton;
#property (nonatomic, retain) UISlider *volumeSlider;
#property (nonatomic, retain) UISlider *progressBar;
#property (nonatomic, retain) UILabel *currentTime;
#property (nonatomic, retain) UILabel *duration;
#property (nonatomic, retain) NSTimer *updateTimer;
#property (nonatomic, assign) AVAudioPlayer *player;
#property (nonatomic, assign) BOOL inBackground;
#end
FirstViewController.m code:
// amount to skip on rewind or fast forward
#define SKIP_TIME 1.0
// amount to play between skips
#define SKIP_INTERVAL .2
#implementation RygestopFirstViewController
#synthesize playButton;
#synthesize volumeSlider;
#synthesize progressBar;
#synthesize currentTime;
#synthesize duration;
#synthesize updateTimer;
#synthesize player;
#synthesize inBackground;
-(void)updateCurrentTimeForPlayer:(AVAudioPlayer *)p
{
currentTime.text = [NSString stringWithFormat:#"%d:%02d", (int)p.currentTime / 60, (int)p.currentTime % 60, nil];
progressBar.value = p.currentTime;
}
- (void)updateCurrentTime
{
[self updateCurrentTimeForPlayer:self.player];
}
- (void)updateViewForPlayerState:(AVAudioPlayer *)p
{
[self updateCurrentTimeForPlayer:p];
if (updateTimer)
[updateTimer invalidate];
if (p.playing)
{
[playButton setImage:((p.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
updateTimer = [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:#selector(updateCurrentTime) userInfo:p repeats:YES];
}
else
{
[playButton setImage:((p.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
updateTimer = nil;
}
}
- (void)updateViewForPlayerStateInBackground:(AVAudioPlayer *)p
{
[self updateCurrentTimeForPlayer:p];
if (p.playing)
{
[playButton setImage:((p.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
}
else
{
[playButton setImage:((p.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
}
}
-(void)updateViewForPlayerInfo:(AVAudioPlayer*)p
{
duration.text = [NSString stringWithFormat:#"%d:%02d", (int)p.duration / 60, (int)p.duration % 60, nil];
progressBar.maximumValue = p.duration;
volumeSlider.value = p.volume;
}
-(void)pausePlaybackForPlayer:(AVAudioPlayer*)p
{
[p pause];
[self updateViewForPlayerState:p];
}
-(void)startPlaybackForPlayer:(AVAudioPlayer*)p
{
if ([p play])
{
[self updateViewForPlayerState:p];
}
else
NSLog(#"Could not play %#\n", p.url);
}
- (IBAction)playButtonPressed:(UIButton *)sender
{
if (player.playing == YES)
[self pausePlaybackForPlayer: player];
else
[self startPlaybackForPlayer: player];
}
- (IBAction)volumeSliderMoved:(UISlider *)sender
{
player.volume = [sender value];
}
- (IBAction)progressSliderMoved:(UISlider *)sender
{
player.currentTime = sender.value;
[self updateCurrentTimeForPlayer:player];
}
- (void)dealloc
{
[super dealloc];
[playButton release];
[volumeSlider release];
[progressBar release];
[currentTime release];
[duration release];
[updateTimer release];
[player release];
[playBtnBG release];
[pauseBtnBG release];
}
#pragma mark AVAudioPlayer delegate methods
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)p successfully:(BOOL)flag
{
if (flag == NO)
NSLog(#"Playback finished unsuccessfully");
[p setCurrentTime:0.];
if (inBackground)
{
[self updateViewForPlayerStateInBackground:p];
}
else
{
[self updateViewForPlayerState:p];
}
}
- (void)playerDecodeErrorDidOccur:(AVAudioPlayer *)p error:(NSError *)error
{
NSLog(#"ERROR IN DECODE: %#\n", error);
}
// we will only get these notifications if playback was interrupted
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)p
{
NSLog(#"Interruption begin. Updating UI for new state");
// the object has already been paused, we just need to update UI
if (inBackground)
{
[self updateViewForPlayerStateInBackground:p];
}
else
{
[self updateViewForPlayerState:p];
}
}
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)p
{
NSLog(#"Interruption ended. Resuming playback");
[self startPlaybackForPlayer:p];
}
#pragma mark background notifications
- (void)registerForBackgroundNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(setInBackgroundFlag)
name:UIApplicationWillResignActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(clearInBackgroundFlag)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
- (void)setInBackgroundFlag
{
inBackground = true;
}
- (void)clearInBackgroundFlag
{
inBackground = false;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Play", #"First");
self.tabBarItem.image = [UIImage imageNamed:#"Home"];
}
return self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
//Make sure we can recieve remote control events
- (BOOL)canBecomeFirstResponder {
return YES;
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
playBtnBG = [[UIImage imageNamed:#"Player.png"] retain];
pauseBtnBG = [[UIImage imageNamed:#"Pause.png"] retain];
[playButton setImage:playBtnBG forState:UIControlStateNormal];
[self registerForBackgroundNotifications];
updateTimer = nil;
duration.adjustsFontSizeToFitWidth = YES;
currentTime.adjustsFontSizeToFitWidth = YES;
progressBar.minimumValue = 0.0;
// Load the the sample file, use mono or stero sample
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: [[NSBundle mainBundle] pathForResource:#"Sound1" ofType:#"m4a"]];
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
if (self.player)
{
[self updateViewForPlayerInfo:player];
[self updateViewForPlayerState:player];
player.numberOfLoops = 0;
player.delegate = self;
}
[fileURL release];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
And, by the way, i want to run thin in tab bar application, so the background mode must be present always.
Here's what you're looking for: https://devforums.apple.com/message/264397 and set your background mode to 'App plays audio' in your app's .plist file.

Resources