I create an iPhone application (with a view) / Watch (with interface) that displays speed, distance and a timer with a Play / Pause, Stop and Clear.
I used to share WatchConnectivity Application Context data between (Send and receipt of Context). Everything works so far, but I would add another page / interfaces on which exchanges Watch the Context with the iPhone and there I do not know at all how.
My Interface Storyboard
If I turn on the Session 2 interfaces, information is lost
Here is my current code, I want to toggle the display of labels TimeLabel, distanceLabel Label and speed on the second interface
//
// InterfaceController.m
// Watch Extension
//
// Created by Arnaud Roy on 25/10/2015.
// Copyright © 2015 Burotica. All rights reserved.
//
#import "InterfaceController.h"
#import <WatchConnectivity/WatchConnectivity.h>
#interface InterfaceController() <WCSessionDelegate>
#property (strong, nonatomic) WCSession *session;
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceButton *startLabel;
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceLabel *timeLabel;
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceLabel *distanceLabel;
#property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceLabel *vitesseLabel;
#property (nonatomic,assign) BOOL running;
#end
#implementation InterfaceController
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
// Configure interface objects here.
}
- (void)willActivate {
// This method is called when watch view controller is about to be visible to user
[super willActivate];
if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
//NSLog(#"SESSION AVAIBLE");
}
//Objective-C
if ([[WCSession defaultSession] isReachable]) {
//NSLog(#"SESSION REACHABLE");
}
self.running = false;
}
- (void)didDeactivate {
// This method is called when watch view controller is no longer visible
[super didDeactivate];
}
- (IBAction)startButton {
if(self.running == false) {
[self sendCmd:#"start"];
[self.startLabel setTitle:[NSString stringWithFormat:#"PAUSE"]];
self.running = true;
}
else
{
[self sendCmd:#"pause"];
[self.startLabel setTitle:[NSString stringWithFormat:#"PLAY"]];
self.running = false;
}
}
- (IBAction)clearButton {
[self sendCmd:#"clear"];
}
- (IBAction)plusmoinsButton {
[self sendCmd:#"plusmoins"];
}
- (IBAction)stopButton {
[self sendCmd:#"stop"];
}
-(void)sendCmd:(NSString*) cmdSendW{
WCSession *session = [WCSession defaultSession];
NSError *error;
[session updateApplicationContext:#{#"cmdSendW":cmdSendW} error:&error];
}
- (void)session:(nonnull WCSession *)session didReceiveApplicationContext:(nonnull NSDictionary<NSString *,id> *)applicationContext {
NSString *cmdSend = [applicationContext objectForKey:#"cmdSend"];
NSString *timeSend = [applicationContext objectForKey:#"timeSend"];
NSString *distanceSend = [applicationContext objectForKey:#"distanceSend"];
NSString *partielSend = [applicationContext objectForKey:#"partielSend"];
NSString *vitesseSend = [applicationContext objectForKey:#"vitesseSend"];
dispatch_async(dispatch_get_main_queue(), ^{
if([cmdSend isEqual: #"start"])
{
[self.startLabel setTitle:[NSString stringWithFormat:#"PAUSE"]];
[self.timeLabel setText:[NSString stringWithFormat:#"Time : %#", timeSend]];
[self.distanceLabel setText:[NSString stringWithFormat:#"Distance : %#", distanceSend]];
[self.vitesseLabel setText:[NSString stringWithFormat:#"Vitesse : %#", vitesseSend]];
self.running = true;
}
else if([cmdSend isEqual: #"pause"])
{
[self.startLabel setTitle:[NSString stringWithFormat:#"PLAY"]];
[self.timeLabel setText:[NSString stringWithFormat:#"Time : %#", timeSend]];
[self.distanceLabel setText:[NSString stringWithFormat:#"Distance : %#", distanceSend]];
[self.vitesseLabel setText:[NSString stringWithFormat:#"Vitesse : %#", vitesseSend]];
self.running = false;
}
else if([cmdSend isEqual: #"stop"])
{
[self.startLabel setTitle:[NSString stringWithFormat:#"PLAY"]];
[self.timeLabel setText:[NSString stringWithFormat:#"Time : %#", timeSend]];
[self.distanceLabel setText:[NSString stringWithFormat:#"Distance : %#", distanceSend]];
[self.vitesseLabel setText:[NSString stringWithFormat:#"Vitesse : %#", vitesseSend]];
self.running = false;
}
});
}
#end
Thanks for your help
I'd suggest having your UIApplicationDelegate and ExtensionDelegate create an instance of a new object you make that is the WCSessionDelegate. This object will persist incoming data to disk, and post notifications that the backing data store has been updated. Then each of the UI components can monitor for these notifications and update their UIs as appropriate.
Related
I am working with IOT weather connect display(Use Local Network) that connect with the router using iOS app. connection between display and iOS app is done by local network.popup came up at the time of connection for allowing the local network privacy but i want to check in advance that user has allowed or not local network permission. I refer this iOS 14 How to trigger Local Network dialog and check user answer? and https://developer.apple.com/forums/thread/663858
but i am looking for code in Objecitve-C. please help me with that
This is my original answer that is written in Swift - iOS 14 How to trigger Local Network dialog and check user answer? and you can use it from objc as well without any effort.
But if you have a pure ObjC project and don't want to add swift files this is the similar approach that works the same:
// LocalNetworkPrivacy.h
#interface LocalNetworkPrivacy : NSObject
- (void)checkAccessState:(void (^)(BOOL))completion;
#end
// LocalNetworkPrivacy.m
#import <UIKit/UIKit.h>
#import "LocalNetworkPrivacy.h"
#interface LocalNetworkPrivacy () <NSNetServiceDelegate>
#property (nonatomic) NSNetService *service;
#property (nonatomic) void (^completion)(BOOL);
#property (nonatomic) NSTimer *timer;
#property (nonatomic) BOOL publishing;
#end
#implementation LocalNetworkPrivacy
- (instancetype)init {
if (self = [super init]) {
self.service = [[NSNetService alloc] initWithDomain:#"local." type:#"_lnp._tcp." name:#"LocalNetworkPrivacy" port:1100];
}
return self;
}
- (void)dealloc {
[self.service stop];
}
- (void)checkAccessState:(void (^)(BOOL))completion {
self.completion = completion;
self.timer = [NSTimer scheduledTimerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) {
if (UIApplication.sharedApplication.applicationState != UIApplicationStateActive) {
return;
}
if (self.publishing) {
[self.timer invalidate];
self.completion(NO);
}
else {
self.publishing = YES;
self.service.delegate = self;
[self.service publish];
}
}];
}
#pragma mark - NSNetServiceDelegate
- (void)netServiceDidPublish:(NSNetService *)sender {
[self.timer invalidate];
self.completion(YES);
}
#end
How to use:
LocalNetworkPrivacy* localNetworkPrivacy = [LocalNetworkPrivacy new];
[localNetworkPrivacy checkAccessState:^(BOOL granted) {
NSLog(#"Granted: %#", granted ? #"YES" : #"NO");
}];
NOTE: You must set NSLocalNetworkUsageDescription and add "_lnp._tcp." to NSBonjourServices into your Info.plist.
My goal is to achieve synchronized communication to custom Device i.e. next command can be send only when reply is received. Now I'm doing it in this way
Device class implements DeviceDelegate protocol
//Device.h
#class Device;
#protocol DeviceDelegate <NSObject>
- (void)didReciveReplyWithData:(NSData *)data;
#end
#interface Device : NSObject {}
In DeviceViewController implementation:
#interface DeviceViewController()
{
BOOL waitingForReply = false;
}
#end
#implementation DeviceViewController
- (void)sendCommandWithData:(NSData *)data
{
if ( waitingForReply == false)
{
//send command code
waitingForReply = true;
}
}
- (void)didReciveReplyWithData:(NSData *)data
{
//code
waitingForReply = false;
}
#end
but I wish to do it in more elegant way i.e. by using GCD (semaphores?) with blocks (completionHandler?). Any ideas?
PS. Sorry, but I forgot to mention: all commands sended to device while
waitingForReply = true
should be ignored!!!.
Possibly the best approach here would be to create a queue of commands with NSOperationQueue.
Since, presumably, the communication with the device is asynchronous you will have to subclass NSOperation to encapsulate the communication.
#interface DeviceCommandOperation : NSOperation <DeviceDelegate>
#property (nonatomic, assign) BOOL waitingForReply;
#property (nonatomic, copy) NSData *dataToSend;
#property (nonatomic, copy) NSData *dataReceived;
#end
#implementation DeviceCommandOperation
- (instancetype)initWithData:(NSData *)dataToSend
{
self = [super init];
if (self)
{
_dataToSend = [dataToSend copy];
}
return self;
}
- (void)setWaitingForReply:(BOOL)waitingForReply
{
if (_waitingForReply != waitingForReply)
{
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_waitingForReply = waitingForReply;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
}
}
- (void)start
{
self.waitingForReply = YES;
// Simulate sending a command and waiting for response.
// You will need to replace this with your actual communication mechanism.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// In reality this call would presumably come from the Device
[self didReceiveReplyWithData:someData];
});
}
- (void)didReceiveReplyWithData:(NSData *)data
{
self.dataReceived = data;
self.waitingForReply = NO;
}
#pragma mark - NSOperation
- (BOOL)isAsynchronous
{
return YES;
}
- (BOOL)isExecuting
{
return _waitingForReply;
}
- (BOOL)isFinished
{
return !_waitingForReply;
}
#end
This operation could then be used from your DeviceViewController (it would probably be better architecturally to have this responsibility elsewhere but that's not the topic of this question).
#interface DeviceViewController ()
#property (nonatomic, strong) NSOperationQueue *operationQueue;
#end
#implementation DeviceViewController
- (NSOperationQueue *)operationQueue
{
if (_operationQueue == nil)
{
_operationQueue = [[NSOperationQueue alloc] init];
}
return _operationQueue;
}
- (void)sendNextCommand
{
NSData *data = // Get data for the next command
[self sendCommandWithData:data];
}
- (void)sendCommandWithData:(NSData *)data
{
NSLog(#"Queueing operation");
DeviceCommandOperation *operation = [[DeviceCommandOperation alloc] initWithData:data];
// The operation's completionBlock gets called on a background queue
[operation setCompletionBlock:^{
NSLog(#"DeviceCommandOperation completed");
// Process operation.dataReceived
[self sendNextCommand];
}];
[self.operationQueue addOperation:operation];
}
#end
This approach will allow you to determine what (if any) command to send next, based on the reply to the previous command.
If you know all of the "commands" you will want to send initially and don't need finer grained control you could create instances of DeviceCommandOperation for each command, set the queue's maxConcurrentOperationCount to 1, and add each DeviceCommandOperation to the queue (in the order you want them to be processed).
Please be patient. This is my first time setting up an in-app purchase and I will try and provide as much information as I can. Instead of grooming the internet for someone else's code I ended up purchasing a class from infinite skills specifically on how to add a non-renewing subscription in-app purchase to my app. Their class was outdated, and come to find out, the "follow along" project files did not download. So I did a lot of research and this is what I came up with:
I created an in-app purchase in itunes, the in-app identifier matches the identifier in my .m coding.
I created a new provisioning profile and enabled in-app purchases and the icloud to manage the backend for the purchase. When I returned to the app i enabled icloud and made sure in-app purchases was turned on in the project target.
I created a view controller, added buttons and for the subclass I used SKStoreProductViewController. That View Controller looks like this:
I imported the storekit and the SK delegates.
It crashes when I hit the tabbarbutton from my home view to bring me to the in-app view controller.
Finally the coding:
InAppViewController.h:
#import <StoreKit/StoreKit.h>
#interface InAppViewController : SKStoreProductViewController
#end
InAppViewController.m:
//
// InAppViewController.m
// Contractor Rich
//
// Created by Joshua Hart on 2/1/15.
// Copyright (c) 2015 Code By Hart. All rights reserved.
//
#import "InAppViewController.h"
#import <StoreKit/StoreKit.h>
#interface InAppViewController ()<SKProductsRequestDelegate, SKPaymentTransactionObserver>
#property (weak, nonatomic) IBOutlet UIButton *btnBuyAccess;
#property (weak, nonatomic) IBOutlet UIButton *btnPremiumFeature;
#property (weak, nonatomic) IBOutlet UILabel *lblStatus;
#property (strong, nonatomic) SKProduct *product;
#property (strong, nonatomic) NSUbiquitousKeyValueStore *keyStore;
#property (strong, nonatomic) NSDate *expirationDate;
- (IBAction)btnBuyAccessTouched: (id)sender;
-(void)getProductInfo;
#end
#implementation InAppViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_btnBuyAccess.userInteractionEnabled = NO;
_keyStore = [[NSUbiquitousKeyValueStore alloc] init];
_expirationDate = [_keyStore objectForKey:#"expirationDate"];
NSDate *today = [NSDate date];
if (_expirationDate == nil)
_expirationDate = today;
if (_expirationDate > today) {
_btnPremiumFeature.userInteractionEnabled = YES;
}
else {
_btnBuyAccess.userInteractionEnabled = YES;
[self getProductInfo];
}
}
-(void) getProductInfo{
if ([SKPaymentQueue canMakePayments])
{
NSMutableArray *productIdentifierList = [[NSMutableArray alloc] init];
[productIdentifierList addObject:[NSString stringWithFormat:#"com.joshua.contractorrich.inapp"]];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithArray:productIdentifierList]];
request.delegate = self;
[request start];
}
}
-(void) productsRequest: (SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray *products = response.products;
if (products.count != 0)
{
_product = products[0];
_btnBuyAccess.userInteractionEnabled = YES;
_lblStatus.text = #"Ready for Purchase!";
}else{
_lblStatus.text = #"Product was not Found!";
}
products = response.invalidProductIdentifiers;
for (SKProduct *product in products)
{
NSLog(#"Product not found: %#", product);
}
}
- (IBAction)btnBuyAccessTouched: (id)sender {
SKPayment *payment = [SKPayment paymentWithProduct:_product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
_btnPremiumFeature.userInteractionEnabled = YES;
_lblStatus.text = #"Purchase Completed!";
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed: NSLog(#"Transaction Failed!");
_lblStatus.text = #"Purchase Failed!";
[[SKPaymentQueue defaultQueue]
finishTransaction:transaction];
default:
break;
}
}
}
-(void) setExpirationDate {
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setMonth:3];
NSDate *expirationDate = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:[NSDate date] options:0];
[_keyStore setObject:expirationDate forKey:#"expirationDate"];
[_keyStore synchronize];
}
-(void) initializeStore {
[_keyStore setObject:nil forKey:#"expirationDate"];
[_keyStore synchronize];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
Please understand this is my first time trying this. What may seem stupid is still the learning phase for me. Thank you!
There is no runtime error in this story. All that's happened is that you've created an Exceptions breakpoint. Now you are pausing at it. Simply resume running. If this is troublesome, disable the Exceptions breakpoint.
I finally got my leaderboard to show up. Now I just need to implement that my score will pop up.
My score is saved as an NSString in NSUserDefaults under the name score.
Here is some code:
Game_CenterViewController.h
#import <UIKit/UIKit.h>
#import <GameKit/GameKit.h>
#import "GameCenterManager.h"
#class GameCenterManager;
#interface Game_CenterViewController : UIViewController <UIActionSheetDelegate, GKLeaderboardViewControllerDelegate, GKAchievementViewControllerDelegate, GameCenterManagerDelegate> {
GameCenterManager *gameCenterManager;
int64_t currentScore;
NSString *currentLeaderBoard;
}
#property (nonatomic, retain) GameCenterManager *gameCenterManager;
#property (nonatomic, assign) int64_t currentScore;
#property (nonatomic, retain) NSString* currentLeaderBoard;
#end
Game_CenterViewController.m
#import "Game_CenterViewController.h"
#import "AppSpecificValues.h"
#import "GameCenterManager.h"
#implementation Game_CenterViewController
#synthesize gameCenterManager;
#synthesize currentScore;
#synthesize currentLeaderBoard;
- (void)dealloc {
[gameCenterManager release];
[currentLeaderBoard release];
[currentScoreLabel release];
[super dealloc];
}
#pragma mark - View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.currentLeaderBoard = thescore101;
self.currentScore = score
if ([GameCenterManager isGameCenterAvailable]) {
self.gameCenterManager = [[[GameCenterManager alloc] init] autorelease];
[self.gameCenterManager setDelegate:self];
[self.gameCenterManager authenticateLocalUser];
} else {
// The current device does not support Game Center.
}
}
- (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController {
[self dismissModalViewControllerAnimated: YES];
[viewController release];
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.gameCenterManager = nil;
self.currentLeaderBoard = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
The current score is where I'm trying to put the NSString in.
EDITED: I think it would be better to use int for the currentScore.
Is this what you are looking for?
- (void)submitMyScore:(int64_t)score
{
GKScore *myScoreValue = [[[GKScore alloc] initWithCategory:#"yourCat"] autorelease];
myScoreValue.value = (int)score;
[myScoreValue reportScoreWithCompletionHandler:^(NSError *error){
if(error != nil){
NSLog(#"Score Submission Failed");
} else {
NSLog(#"Score Submitted: %d",(int)score);
}
}];
}
So you should add a IBAction
- (IBAction)buttonPressed
{
[self submitMyScore:currentScore];
}
With this and connecting the SEND MY SCORE button to this IBAction, you will have your score submitted.
I hope this to be useful for you.
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.