Detect when GameCenter UI is displayed - iphone

I'm trying to integrate my game with Game Center and encountered this problem:
When user is authenticated for a first time, Game Center shows its UI for setting up the profile.
My problem is that I can not detect when this windows is shown - I want to pause my game at that moment and not play any sounds.
viewWillDisapper, viewDidDisapper in UIViewController are not called, neither are any of AppDelegate methods are called at this time.
I think I know how detect alert views (using changing key window notification), but that Account windows still is not detected there.
Is there any way to do this?

Building on executor21's answer here, I put this together which seems to do the trick in early testing. You can probably adapt it into something less fragile. It is built on the premise that the Game Center notification gets its own window, and it has exactly one subview of type GKGameEventView:
+(BOOL)isGameCenterNotificationUp
{
NSArray *windows = [[UIApplication sharedApplication] windows];
for(UIWindow *win in windows)
{
NSArray *winSubViews = [win subviews];
if([winSubViews count] == 1)
{
Class gcNotificationClass = NSClassFromString(#"GKGameEventView");
if(gcNotificationClass && ([[winSubViews objectAtIndex:0] isKindOfClass:gcNotificationClass]))
{
return YES;
}
}
}
return NO;
}

Related

Is MPNowPlayingInfoCenter compatible with AVAudioPlayer?

I start -play with AVAudioPlayer, and then set the nowPlaying dictionary like this:
NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
        
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage: [UIImage imagedNamed:#"AlbumArt"]];
[songInfo setObject:#"Audio Title" forKey:MPMediaItemPropertyTitle];
[songInfo setObject:#"Audio Author" forKey:MPMediaItemPropertyArtist];
[songInfo setObject:#"Audio Album" forKey:MPMediaItemPropertyAlbumTitle];
[songInfo setObject:albumArt forKey:MPMediaItemPropertyArtwork];
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];
The lock screen always shows a pause button. I receive the remote control events correctly and I can toggle play/pause through the remote control events, but the lock screen keeps showing "pause" even when it is playing.
Now I seen this work with MPMoviePlayerController. Can someone explain how does the MPNowPlayingInfoCenter determine if it should show a play or a pause button?
Have you set the correct AVAudioSessionCategory on the AudioSession? it needs to be AVAudioSessionCategoryPlayback I believe to get it to work.
I'm not using MPNowPlaying at the moment, but apparently I have to, in order to get the audio info displayed on the lock screen.
However, in addition to what #user3061915 said, to manage the play/pause button, I've used UIEventTypeRemoteControl and it works perfect for controlling the play/pause button:
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
//if it is a remote control event handle it correctly
if (event.type == UIEventTypeRemoteControl)
{
if (event.subtype == UIEventSubtypeRemoteControlPlay)
{
[self playAudio];
}
else if (event.subtype == UIEventSubtypeRemoteControlPause)
{
[self pauseAudio];
}
else if (event.subtype == UIEventSubtypeRemoteControlTogglePlayPause)
{
[self togglePlayPause]; //This method will handle the toggling.
}
}
I just fixed a problem like this in my own app. I originally used [[AVAudioSession sharedInstance] setCategory:withOptions:error:] and supplied AVAudioSessionCategoryOptionMixWithOthers and AVAudioSessionCategoryOptionDuckOthers. This turned out to be my problem. If you set mix with others, you get no remote control events. They still go to the iPod app. If you set duck others, you get remote control events, but it appears as though it causes the problem you describe: the play/pause button shows the wrong thing. I'm not sure why. I got the play/pause button to behave by setting options to 0, or actually just calling setCategory:error:.

How can I stop views on screen from multiplying? (touchesBegan,touchesMoved,touchesEnded)

I am new to Objective C and I am trying to make a basic puzzle game. I am using multiple UIImage objects, some labels and a button. It consists in moving the pieces to its correct position. If the user fails to put a piece in a place, the piece goes back to its original location.
That part works perfectly, the problem begins once I 'remove' that view and continue on the other screens. Whenever I click ANYWHERE on screen the views get multiplied endlessly. Why is that happening?
What should I do?, I am so desperate, I've tried almost everything including ignoring touches, blocking touches, etc. And this only creates more trouble.
I have already tried setting using interactions to no, ignoring interaction events, autoreleasing the view, and I have not reached a solution.
Any help will be very much appreciated. Greetings!
---> I tried posting an image but given that I am a newbie I wasn't able to, however I can send the image to anyone if needed to show you the weird effect I get.
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesCancelled:touches withEvent:event];
}
-(IBAction) navigationConversation: (id)sender{
//[self.view.superview setUserInteractionEnabled:NO];
//[[UIApplication sharedApplication] endIgnoringInteractionEvents];
if (nextButton.hidden == NO) {
conversation *navigationConversationController=[[conversation alloc] initWithNibName:#"conversation" bundle:nil];
[self.view addSubview:navigationConversationController.view];
if (self.view.superview == nil)
[navigationConversationController autorelease];
}
return;
}
Thank God, the problem is solved now. This are the changes I made, and this corrected the mistake. :)
-(IBAction) navigationConversation: (id)sender{
if (nextButton.hidden == NO) {
//I disabled the interactions with the puzzle view in here, thus eliminating the problem
[self.view.superview setUserInteractionEnabled:NO];
conversation *navigationConversationController=[[conversation alloc] initWithNibName:#"conversation" bundle:nil];
[self.view addSubview:navigationConversationController.view];
if (self.view.superview == nil)
[navigationConversationController autorelease];
}
return;
}
Thanks to everyone who looked! and wanted to help! I hope this works for other people having the same issues.

Is there any notification for detecting AirPlay in Objective-C?

I am using MPVolumeView for showing Airplay icon and it works fine.
But I need to show an animation when Airplay network comes, and hide that animation when airplay network hides.
Is there a notification that will let me know when Airplay starts and ends?
This is exactly what you're looking for - https://github.com/StevePotter/AirPlayDetector
It is a single class that provides a property to determine whether airplay devices are active. And a notification when availability changes.
Using it is simple. Like, to determine availability you write:
[AirPlayDetector defaultDetector].isAirPlayAvailable
Enjoy!
To be precise:
To check exactly for airplay with public API: NO
All you can do with public API is to check for available wireless routes, which includes airplay in it: (In simple case when you have a MPVolumeView instance hooked up somewhere to your view, you can just call volumeView.areWirelessRoutesAvailable;)
If you are curious how to check if exactly airplay is available with private API:
- (BOOL)isAirplayAvailable
{
Class MPAVRoutingController = NSClassFromString(#"MPAVRoutingController");
id routingController = [[MPAVRoutingController alloc] init];
NSArray* availableRoutes = [routingController performSelector:#selector(availableRoutes)];
for (id route in availableRoutes) {
NSDictionary* routeDescription = [route performSelector:#selector(avRouteDescription)];
if ([routeDescription[#"AVAudioRouteName"] isEqualToString:#"AirTunes"])
return true;
}
return false;
}
(And in fact MPVolumeView has an MPAVRoutingController instance as its ivar, so the -areWirelessRoutesAvailable is just an accessor exactly for [volumeView->_routingController wirelessDisplayRoutesAvailable])
Also AVAudioSession exposes currentRoute to you, so you do can check if airplay is active easily with:
- (BOOL)isAudioSessionUsingAirplayOutputRoute
{
AVAudioSession* audioSession = [AVAudioSession sharedInstance];
AVAudioSessionRouteDescription* currentRoute = audioSession.currentRoute;
for (AVAudioSessionPortDescription* outputPort in currentRoute.outputs){
if ([outputPort.portType isEqualToString:AVAudioSessionPortAirPlay])
return true;
}
return false;
}
(the answer about AirPlayDetector doesn't guarantee that Airplay is available - all it does it checks the alpha value of MPVolumeView's routeSelection button, which will be shown in any case when wireless routes are available, bluetooth for example. It will do exactly the same as volumeView.areWirelessRoutesAvailable;)
There's a MPVolumeViewWirelessRoutesAvailableDidChangeNotification since iOS 7 you can register for.
It can be done much easier with ReactiveCocoa. Check it out:
MPVolumeView *myVolumeView = [[MPVolumeView alloc] initWithFrame:CGRectMake(0, 0, 180, 22)];
for (UIView *view in myVolumeView.subviews) {
if ([view isKindOfClass:[UIButton class]]) {
[[RACAbleWithStart(view, alpha) distinctUntilChanged] subscribeNext:^(id x) {
NSLog(#"airplay button visibility changed %#", x);
}];
[[RACAbleWithStart(view, frame) distinctUntilChanged] subscribeNext:^(id x) {
NSLog(#"airplay button connection changed %#", x);
}];
}
}
6 years later.
I think Sankar Siva did not ask for detecting, but for activating an airplay route.
I've upped #Alf because he placed me on the right direction, but he is not answering to the question.
MPVolumeViewWirelessRoutesAvailableDidChangeNotification fires when MPVolumeView detects a new route.
On the other hand, MPVolumeViewWirelessRouteActiveDidChangeNotification fires when a new route is taken, eg: when you select your Apple TV for example.
No need of private API.
If you want a notification here is the way to do it
[[NSNotificationCenter defaultCenter]
addObserver:self
selector: #selector(deviceChanged:)
name:AVAudioSessionRouteChangeNotification
object:[AVAudioSession sharedInstance]];
- (void)deviceChanged:(NSNotification *)sender {
NSLog(#"Enters here when connect or disconnect from Airplay");
}

saving data in iPhone development

I have some unsolved problem right now. My game is done, now I am at the last stage. In the main menu of the game there will be a button called goodies. Only appears when the user win the game once. I can do until that part . But the last part is to save that goodies. Meaning when the player exist the game after he able to make the button appear, the button still there when he gets back to the game? Could you guys help me out I am not quite sure how to do it. I heard a lot about NSUSerDefault, but don know how it works and how to apply it properly. Thanks for any help.
DegrafeurAppDelegate *appdelegate = (DegrafeurAppDelegate *) [[UIApplication sharedApplication] delegate];
//BOOL b = appdelegate.checkStatus;
if(appdelegate.checkStatus == YES)
{
[goodies setVisible:YES];
}
else
{
[goodies setVisible:NO];
}
This is my code to enable the button after winning once. But how to save it, please help me
As you mentioned, you can user NSUserDefaults:
NSUserDefaults myPrefs = [[NSUserDefaults alloc] init];
[myPrefs setBool:YES forKey:#"displayed_win_button"];
[myPrefs release];
and later to check the value:
if([myPrefs boolForKey:#"displayed_win_button"]) { ... }

iPhone: Get a list of clickable areas on screen in cocoa touch

I have an iPhone app and I want to get a list of co-ords that are clickable by a user. I want to automate testing and have a client app click around on screen but just choosing random coords isn't ideal so a list of coords that are definitely clickable would be much better.
So far I have this the view passed in is top level window:
getSubViewsCoords:(UIView *)view {
iAppDelegate* appDelegate = [[UIApplication sharedApplication] delegate];
for (UIView *tempView in [view subviews]) {
// check if view responds to touch i.e. is an interactive view.
// go up through responder chain
// check if it reacts
// if it does then add to the gorilla's list
UIView *responderObj = tempView;
while (responderObj = [responderObj nextResponder]) {
if([responderObj respondsToSelector:#selector(touchesBegan:withEvent:)]){
// get xy etc and add to instance var
//then recall this function
// convert to top level windows co-ordinate system.
CGRect viewRect = [tempView convertRect:[tempView bounds] toView:nil];
clickableAreas = [clickableAreas stringByAppendingString: NSStringFromCGRect(viewRect)];
clickableAreas = [clickableAreas stringByAppendingString: #"\n"];
break;
}
}
[self getSubViewsCoords:tempView];
Is this the correct way to go about it? Is there an easier way to get this information?
Thanks in advance for your help.
If you haven't already read it, you'll probably find this article helpful, Automated user interface testing on the iPhone.