I'm working on an ios app with uitabviewcontroller that plays some music. I don't want each tab viewcontroller to create it's own audio player. I want to have one single audio player and have all the viewcontrollers share it.
so I have created a class called player, which would initiate avaudioplayer with the song url and plays the song,
#import <AVFoundation/AVFoundation.h>
#interface player : NSObject {
AVAudioPlayer *theMainAudio;
}
-(void)playSong:(NSString *)songName;
#end
I want to create only one instance of this class and all my viewcontrollers share it. I've tried creating it in my delegate,
#interface AppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
UIWindow *window;
UITabBarController *tabBarController;
player *theMainPlayer;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
#property (nonatomic, retain) player *theMainPlayer;
#end
in .m file,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//some other stuff here....
theMainPlayer = [[player alloc]init];
return YES;
}
and then I called it in my viewcontrollers,
player myPlayer = ((AppDelegate *)[UIApplication sharedApplication].delegate).theMainPlayer;
but this didn't work.
can anyone tell me what's wrong with what I've done or if there is any other way to do what I want to do, which is to create a player object and share it among all of my viewcontrollers.
Thanks
create a singleton, in your player.m
#import "player.h"
#implementation player
static player *sharedInstance = nil;
+ (player *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
- (id)init
{
self = [super init];
if (self) {
// Work your initialising here as you normally would
}
return self;
}
-(void)playSong:(NSString *)songName
{
// do your stuff here
}
to use this class, just import the player.h
[[player sharedInstance] playSong:#"something"];
How did it go wrong? To me it seems the last line would cause compile error. You should add asterisk for class declaration :
player * myPlayer = ((AppDelegate *)[UIApplication sharedApplication].delegate).theMainPlayer;
Other than that, I didn't catch anything.
But I prefer using Singleton pattern for cases like this.
Reference :
http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/Singleton.html
http://www.galloway.me.uk/tutorials/singleton-classes/
Related
I am trying to make a small game where users answer questions. The overall flow will go like this:
Select a difficulty level by clicking a button (push segue)
Player 1 enters name, press "Next" (push segue)
Answer question
Answer question
Finish.
Player 2 enters name, press "Next" (push segue)
Answer question
Answer question
Finish.
Look at everyone's answers
Get sent back to #1
My problem is I don't really need to store user info (name, answers, etc) in a database since that data is useless after the round is over. However, I need it to persist enough so that I can access that data across the view controllers before the round is over.
For the difficulty level I'm using a custom property on AppDelegate to persist the setting:
AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) NSString *difficultyLevel;
#end
DifficultyViewController.m
- (IBAction)setDifficultyLevel:(id)sender {
UIButton *button = (UIButton *)sender;
NSString *difficulty = [[button titleLabel] text];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.difficultyLevel = difficulty;
}
PlayerProfileViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"Edmund: %#", appDelegate.difficultyLevel);
}
However, this doesn't feel very scalable since I am going to have many other properties and I don't feel like that's what AppDelegate is meant to be used for.
Is there a common way to persist data for this sort of thing?
I think you need to have a kind of game manager. A manager is commonly used as a singleton that means you will have only one instance of this class. It could be something like:
-header file:
#interface GameManager : NSObject
#property (nonatomic, strong) NSString *difficultyLevel;
#property (nonatomic, strong) NSString *player1Name;
#property (nonatomic, strong) NSString *player2Name;
#end
-source file:
#implementation GameManager
#synthesize difficultyLevel, player1Name, player2Name;
+ (id)sharedManager {
static GameManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {
// add what you need here
}
return self;
}
#end
And so you will update your code like that:
- (IBAction)setDifficultyLevel:(id)sender {
UIButton *button = (UIButton *)sender;
NSString *difficulty = [[button titleLabel] text];
GameManager *gameManager = [GameManager sharedInstance];
gameManager.difficultyLevel = difficulty;
}
You can also have a weak property on your manager in each view controller if you prefer.
I am currently learning with SpriteKit. I have a background music player instance at main ViewController. However, when I tried to access back to it from SKScene class, I cannot stop/change the player.
ViewController *vc = (ViewController *) self.view.window.rootViewController;
AVAudioPlayer *player = vc.backgroundMusicPlayer;
[player stop]; //nothing happened
I am new to iOS dev, what is the way for managing SpriteKit background music player? Thanks!
I would recommend using a background music player singleton, e.g.
BackgroundMusicPlayer.h
#interface BackgroundMusicPlayer : NSObject
+ (instancetype)sharedPlayer;
#property (nonatomic, strong) AVAudioPlayer *audioPlayer;
#end
BackgroundMusicPlayer.m
#implementation BackgroundMusicPlayer
+ (instancetype)sharedPlayer
{
static BackgroundMusicPlayer *sharedPlayer;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedPlayer = [self new];
});
return sharedPlayer;
}
#end
Then you can access your audio player from anywhere by calling [[BackgroundMusicPlayer sharedPlayer] audioPlayer].
It´s only possible to give the AVAudioPlayer an URL of the file to play via it´s init method.
And as I understand it if one want´s to play another file, the old instance needs to be stopped from playing, and a new instance of AVAudioPlayer initialized with the URL to the new audiofile to play.
But this is making it hard cause I have a navigation controller, and when the user leaves the player screen the sound should keep playing, and it does. But when the user selects a new audio file to play from the tableview a new instance of the viewController and AVAudioPlayer is initalized and I have no way of stopping the old from playing. How do I get this working?
You can do something like this
In your v1AppDelegate.h file add,
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#include <AudioToolbox/AudioToolbox.h>
#interface v1AppDelegate : UIResponder <UIApplicationDelegate>
{
AVAudioPlayer *myAudioPlayer;
}
#property (nonatomic, retain) AVAudioPlayer *myAudioPlayer;
#property (strong, nonatomic) UIWindow *window;
#end
Now in your v1AppDelegate.m file add this
#import "v1AppDelegate.h"
#import <AVFoundation/AVFoundation.h>
#include <AudioToolbox/AudioToolbox.h>
#implementation v1AppDelegate
#synthesize window = _window;
#synthesize myAudioPlayer;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//start a background sound
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"Startsound" ofType: #"m4a"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:soundFilePath ];
myAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
myAudioPlayer.numberOfLoops = -1; //infinite loop
[myAudioPlayer play];
// Override point for customization after application launch.
return YES;
}
If you wish to stop or start this music from anywhere else in your code then simply add this
#import "v1AppDelegate.h"
- (IBAction)stopMusic
{
v1AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate.myAudioPlayer stop];
}
- (IBAction)startMusic
{
v1AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate.myAudioPlayer play];
}
I am programming a Quiz-App. In the Menu-ViewController I added Music to the project. The musicPlayer is running well and as long as the Menu-ViewController is in front, I can also controle it (play, pause, stop, ect.). When I display another ViewController, the Music runs in the Background, like I'd like to. But if try to call the play/pause Method of the first ViewController being in the secondViewController, the music nether paused nor stoped. I don't know why! If I add other Instructions to this Method, everything's going fine. (I tried the exit(0); Method. Here is my Code:
Controller 1 .h :
#implementation MenuViewController : <....> {
... }
#property (retain) AVAudioPlayer *backgroundPlayer;
- (void) playPauseMethod;
Controller 1 .m :
#interface ...
#end
#implementation MenuViewController
# synthesize
- (void) soundChanger {
if (hintergrundPlayer.isPlaying) {
[backgroundPlayer pause];}
else if (!backgroundPlayer.isPlaying) {
[backgroundPlayer play];}}
Controller 2 .h :
#import "MenuViewController.h"
#interface QuizViewController : UIViewController{}
Controller 2 .m :
#interface ...
#end
#implementation MenuViewController
# synthesize ...
//..... musicPlayer is playing music.
- (IBAction)myMusic:(id)sender {
//first try:
[[[MenuViewController alloc] init].backgroundPlayer pause];
//second try:
[[[MenuViewController alloc] init] soundChanger];}
I'd like to control the music in every ViewController. I'm looking forward to your help.
You're creating a completely new MenuViewController in Controller2 with
[[MenuViewController alloc] init]
The best way to handle it would be to set up a protocol in controller 2 with something like
#protocol <Controller2Delegate>
-(void) playButtonPressed:(id)self;
#end
Then set up a delegate property (still in controller 2) like this:
#property (weak) id <Controller2Delegate> delegate;
Then, back in Controller 1, when you create the Controller 2, set it's delegate property, like this:
QuizViewController *controller2 = [[QuizViewController alloc] init]];
controller2.delegate = self;
And then create the playButtonPressed method somewhere in controller 1. From Controller 2, you'll do something like:
[self.delegate playButtonPressed:self];
And that will call the method in Controller 1, where you can pause the background player.
I have a UIButton in MainViewController.
MainViewController has a childViewContoller.
I need to access the UIButton (tcButton) property in MainViewController FROM the childViewController and set it to setSelected:YES in viewDidLoad. I have the following code in my ChildViewController.m file and it's not working.
#import "ChildViewController.h"
#import "MainViewController.h"
#import "CoreData.h"
#interface ChildViewContoller ()
#property (nonatomic, strong) CoreData *coreData;
#property (nonatomic, strong) MainViewController *mainViewController;
#end
#implementation ChildViewController
#synthesize coreData, mainViewController;
-(void)viewDidLoad
{
[super viewDidLoad];
self.managedObjectContext = [(STAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
[[(mainViewController *)self.parentViewController tcButton] setSelected:YES];
}
Your code is kind of a mess. Why are you creating a new instance of yourself in viewDidLoad? This makes no sense. If ChildViewController is truly a child view controller, then you can access the parent with self.parentViewController. You only need one line in the viewDidLoad:
-(void)viewDidLoad // Line 4
{
[[(MainViewController *)self.parentViewController tcButton] setSelected:YES]; // Line 8
}
There are several issues in your code but the main idea to perform what you want is getting a pointer to the mainViewController. There are many ways to do that but here a simple example how you can implement such thing. For instance in the initializer of the ChildViewContoller you can pass a pointer to the mainViewController:
#interface ChildViewContoller ()
#property (nonatomic, strong) MainViewController *mainViewController;
#end
#implementation ChildViewContoller
- (id)initWithMainViewController:(MainViewController *)mainViewController
{
self = [super init];
if (self)
{
_mainViewController = mainViewController;
}
return self;
}
- (void)viewDidLoad
{
[_mainViewController.tcButton setSelected:YES];
}
#end
Please not that I have not tested the code above but you can get the idea.