I'm pretty new to Objective C but things are progressing well. However, I think I'm missing a key concept relating to how objects are created and messaged. I hope someone can help me.
I'm creating an iPhone app that simply creates a MPMusicPlayer, and starts playing a song from the que.
I create the music player (myPlayer) in the AppDelegate like so...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Add the main view controller's view to the window and display.
[window addSubview:mainViewController.view];
[window makeKeyAndVisible];
// instantiate a music player
MPMusicPlayerController *myPlayer =
[MPMusicPlayerController applicationMusicPlayer];
// assign a playback queue containing all media items on the device
[myPlayer setQueueWithQuery: [MPMediaQuery songsQuery]];
// start playing from the beginning of the queue
[myPlayer play];
return YES;
}
This works fine. Now in the MainViewController I try to send myPlayer the command to skip to the next track
[myPlayer skipToNextItem];
However when I try to compile I get the message that myPlayer in undeclared.
What am I doing wrong? I can see how I could fix this in a procedural way (by creating the player in the MainViewController), but I'd like to understand what I have to do to get it working in and OOP way.
Cheers,
Richard
Most propably, the mPlayer object is unknown to your ViewController. There are two options for you:
Make the mPlayer a property of your app delegate
Make the mPlayer a property of your view controller subclass and set it to your mPlayer upon creation
In your appdelegates declaration, do:
#property(nonatomic, retain) MPMusicPlayerController *mPlayer;
In your appdelegates implementation, do:
#synthesize mPlayer;
In your viewcontroller, do:
MPMusicPlayerController *mPlayer = [[[UIApplication sharedApplication] delegate] mPlayer];
Related
I am developing a Universal app where I have
imageTracker_iPhone.xib
imageTracker_iPad.xib
imageTracker.h
imageTracker.m
I want to move from AppDelegate_iPhone to imageTracker. I am doing this in didFinishLaunchingWithOptions but its not working before this code I was using
imageTracker *vRDi = [[imageTracker alloc] initWithNibName:#"imageTracker_iPhone" bundle:nil];
[self.view addSubview:vRDi.view];
but it gave error request for member 'view' in something not a structure or union
. Even if code is like
[window addSubview:vRDi.view];
now The function is like below and its not working. I want to move from AppDeligate to imageTracker. please help
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window addSubview:imageTracker.view];
[self.window makeKeyAndVisible];
return YES;
}
In this case It does not move to imageTracker_iPhone because did not tell any where to move to this file, so want to know that HOw to tell that which file to move either imageTracker_iPhone or imageTracker_iPad.
You probably want to set the delegate window's rootViewController to make your first controller active. (If you create a new test app from a single controller, non-storyboard template, you can see the kind of code that's needed in didFinishLaunchingWithOptions:.)
Edit: Actually, it's even easier than that. If you specify a universal app when creating a single view controller project, it creates the exact code to test which kind of device and load the matching .xib file. (Xcode 4.2, at least.)
your code should be something like this
imageTracker *vRDi;
Bool isiPhone = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone;
if(isiPhone)
vRDi = [[imageTracker alloc] initWithNibName:#"imageTracker_iPhone";
else
vRDi = [[imageTracker alloc] initWithNibName:#"imageTracker_iPad";
and make sure that your connect view outlet in both xib's and the file owner is "imageTracker" Class.
I am making a quiz app and i have set different background music in two UIView.
Now i have created button for stop the bachgrond music.
IBOutlet UIButton *soundoffbtn;
I stop the background music for firstview by
-(Void)SoundBtn{
[Avbackmusic stop];
}
It just stop in one view.
When i entered in secondView , background music for that view is started as it should be.
What i want is to stop the background music for whole app (both view) by just tapping the firstview's soundoffbtn
What should i do for that?
I am new to this..
Any help will be appreciated..
Thanks.
I didn't fully understand a question. But if you want the second view not to start playing sound after you switch off sound on first view then create an indicator variable in applicaton delegate that will indicate the sound on/off status for your app. Access that indicator before playing a sound. If it shows on, then play, if off, do not play.
For example in your app delegate create a variable
int soundIndicator;
#property (nonatomic) int soundIndicator;
#synthesize soundIndicator;
Then in your View controllers you can access that variable, it will be common for all application:
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
if (appDelegate.soundIndicator == 0){ ... }
I'm programming an application for the hearing-impaired. I'm hoping to take tracks from the iTunes library, and in my app have a slider for panning. I don't want to use OpenAL (this isn't a game - I repeat this is a media player). So since AVAudioPlayer has the easy pan method, can I take selections from the MPMediaPicker and feed them into the AVAudioPlayer so I can pan them?
I dont do a lot of iOS development, but I believe there are two ways.
Method #1
You need to add /System/Library/Frameworks/AVFoundation.framework to your target in Xcode and #import AVAudioPlayer.h as well as You need to add MediaPlayer.framework to your target in Xcode and #import .
For this operation, you need MPMediaPicker to pass the song data to AVAMedia Player. That can be accomplished like this:
#interface MusicPlayerDemoViewController : UIViewController <MPMediaPickerControllerDelegate> {
...
}
...
// This action should open the media picker
- (IBAction)openMediaPicker:(id)sender;
#end
// MusicPlayerDemoViewController.m
- (IBAction)openMediaPicker:(id)sender {
MPMediaPickerController *mediaPicker = [[MPMediaPickerController alloc] initWithMediaTypes:MPMediaTypeMusic];
mediaPicker.delegate = self;
mediaPicker.allowsPickingMultipleItems = NO; // this is the default
[self presentModalViewController:mediaPicker animated:YES];
[mediaPicker release];
}
// Media picker delegate methods
- (void)mediaPicker: (MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection {
// We need to dismiss the picker
[self dismissModalViewControllerAnimated:YES];
(Code Continues Below, the blanks are for you to fill in)
AT THIS POINT, CALL THE AVAAUDIOPLAYER CLASS AND TELL IT TO PLAY mediaItemCollection . REMEMBER TO STOP AUDIO BEFORE PLAYING, AS IT WILL PLAY MULTIPLE SONGS AT ONCE.
}
- (void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker {
// User did not select anything
// We need to dismiss the picker
[self dismissModalViewControllerAnimated:YES];
}
NOW, ONCE THIS IS DONE THE USER NEEDS TO SELECT A NEW SONG. YOU COULD EITHER CREATE A WHILE LOOP AROUND THE WHOLE THING, WHERE THE CONDITIONAL IS CURRENT TIME >= DURATION (FROM AVAAUDIO PLAYER),
ALTERNATIVELY, YOU COULD CREATE A BUTTON TO OPEN THE PICKER
For more questions checkout:
http://oleb.net/blog/2009/07/the-music-player-framework-in-the-iphone-sdk/ (I used much of their code)
http://developer.apple.com/library/ios/#DOCUMENTATION/AVFoundation/Reference/AVAudioPlayerClassReference/Reference/Reference.html
Good Luck!
Derek
Try having AVAMediaPlayer play the variable mediaItemCollection. This was assigned by the picker to be the song location by the code above. If this does not work make sure that AVAMediaPlayer uses the same input variable type (format, like an ID or a folder location) as MpMediaPicker.
That error message sounds like a technical issue. The only thing I can think of is that AVAAudio player or MPmedia player is looking for a Volume variable (it is required?) and can't find one. I can't really answer this one as I don't do iPhone Development, try there forums or website for some help.
Sounds like you are doing a good job! If you are interested, (I don't know if you are staying at DA) Mr. Cochran (the dean of students at the Upper School) is teaching a iPhone Development Class and a AP Computer Science Class (I am in). If you want to take it further, or you want to just ask questions I know he is more than happy too!
Good Luck! Tell me when it is finished so I can test the results!
I made a little test app to try to isolate this issue, but it exhibits the same behavior: the applicationMusicPlayer stops playing immediately when the app enters the background. I don't know what I'm doing wrong, or if it's an Apple bug. It seems so simple that if it were an Apple bug others would have encountered it and posted on it.
I've set the info.plist UIBackgroundModes key to Audio
I've verified that the app is not terminating
I've tested on 4.1 beta 3 with the same results
I've searched the web for similar complaints. People report other MPMediaPlayerController issues/bugs, but more complex e.g. involving interaction with AVAudio.
Any/all suggestions appreciated.
Here's the core of my test app:
MPTest.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <MediaPlayer/MediaPlayer.h>
#interface MPTestViewController : UIViewController <MPMediaPickerControllerDelegate> {
MPMusicPlayerController *MPPlayer;
}
#property (nonatomic, retain) MPMusicPlayerController *MPPlayer;
#end
MPTest.m
#import "MPTestViewController.h"
#implementation MPTestViewController
#synthesize MPPlayer;
- (void)viewDidLoad {
[super viewDidLoad];
// get the application music player
self.MPPlayer = [MPMusicPlayerController applicationMusicPlayer];
// break to allow application didFinishLaunching to complete
[self performSelector:#selector(presentMPPicker) withObject:nil afterDelay:0.01];
}
- (void)presentMPPicker {
// present the picker in a modal view controller
MPMediaPickerController *picker = [[MPMediaPickerController alloc] initWithMediaTypes:MPMediaTypeAnyAudio];
picker.delegate = self;
[self presentModalViewController:picker animated:YES];
[picker release];
}
// delegate called after user picks a media item
- (void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection {
[self dismissModalViewControllerAnimated:YES];
// tell the player what to play
[MPPlayer setQueueWithItemCollection:mediaItemCollection];
// start playing
[MPPlayer play];
}
#end
MPMusicPlayerController does not support background audio. You will need to use something like AVAudioPlayer or AVPlayer (AVPlayer allows you to use iPod libary items via the AssetURL).
The reason is that MPMusicPlayerController uses the iPod application to play audio, thus your application is not actually playing anything.
Please see this thread on the Apple Developer Forums for more information: https://devforums.apple.com/thread/47905?start=0&tstart=120
Did you set the appropriate audio session type for being a media player? The OS uses session types to make decisions among competing uses for the audio channels.
I'm currently attempting to set up background audio for an app I'm developing for iOS 4. The app doesn't have a dedicated music player viewController, however, unlike other background audio apps such as Pandora, which makes the task a bit more confusing.
I've set the appropriate Info.plist settings correctly and have an AVAudioPlayer object in my app delegate which is accessible from everywhere. When the user plays a song, I replace the AVAudioPlayer with a new one initialized with the song and play it. This all works great, except now I have no idea how to go about supporting remote control events.
Based on Apple's documentation, I have this:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
switch(event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
if([iPhoneAppDelegate backgroundAudioPlayer].playing)
[iPhoneAppDelegate pauseBackgroundAudioPlayer];
else
[iPhoneAppDelegate playBackgroundAudioPlayer];
break;
}
}
The thing is, where do I put this? Apple's documentation seems to suggest this should go in some view controller somewhere, but my app has lots of view controllers and navigation controllers. Wherever I try to put this, for some reason tapping the Toggle Play/Pause button in the multitasking tray remote controls either causes the song to just pause for a moment and then unpause, or somehow causes the song to play twice.
The documentation examples are a bit misleading, but there is no need to subclass anything anywhere. The correct place to put remoteControlReceivedWithEvent: is in the application delegate, as it remains in the responder chain regardless of whether the app is in the foreground or not. Also the begin/end receiving remote control events should be based on whether you actually need the events, not on the visibility of some random view.
I found a couple of solutions to receiving global remote control events on the Apple Developer Forums after a bit of searching.
One way is to subclass UIWindow and override its remoteControlReceivedWithEvent:.
The second, perhaps nicer way is to subclass UIApplication and override sendEvent:. That way, you can intercept all the remote control events and handle them there globally, and not have any other responders handle them later in the responder chain.
- (void)sendEvent:(UIEvent *)event {
if (event.type == UIEventTypeRemoteControl) {
// Handle event
}
else
[super sendEvent:event];
}
The second method didn't work for me, sendEvent was never called. However the first method worked just nicely (subclassing UIWindow).
I struggled with this one for a while and none of the answers above worked. The bug in my code, and I hope that it will help someone reading this, was that I had the AudioSession set to mix with others. You want to be the foreground audio player to get Remote Control events. Check to see if you have INCORRECT code like this:
[[AVAudioSession sharedInstance] setDelegate: self];
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];
UInt32 doSetProperty = 0;
AudioSessionSetProperty (
kAudioSessionProperty_OverrideCategoryMixWithOthers,
sizeof (doSetProperty),
&doSetProperty
);
NSError *activationError = nil;
[[AVAudioSession sharedInstance] setActive: YES error: &activationError];
And remove the AudioSessionSetProperty, or change doSetProperty to 1.
No need to subclass Window or forward events. Simply handle it from your main view controller. See the Audio Mixer (MixerHost) example for details.
http://developer.apple.com/LIBRARY/IOS/#samplecode/MixerHost/Listings/Classes_MixerHostViewController_m.html
Documentation explains it very well.
https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/Remote-ControlEvents/Remote-ControlEvents.html
One thing that seems to influence this behavior is any category options you set for your AVAudioSession using setCategory:withOptions:error: instead of just setCategory:error:. In particular, from trial and error, it appears that if you set AVAudioSessionCategoryOptionMixWithOthers you will not get remote control events; the now playing controls will still control the iPod app. If you set AVAudioSessionCategoryOptionDuckOthers you will get remote control events, but it seems like there may be some ambiguity regarding which app is controlled. Setting the categoryOptions to 0 or just calling setCategory:error: works best.