I'm presenting a GKGameCenterViewController from an SKScene. This is my code:
-(void)showLeaderboardAndAchievements:(BOOL)shouldShowLeaderboard{
GKGameCenterViewController *gcViewController = [[GKGameCenterViewController alloc] init];
gcViewController.viewState = GKGameCenterViewControllerStateLeaderboards;
gcViewController.leaderboardIdentifier = _leaderboardIdentifier;
// _spriteViewController references an UIViewController
[_spriteViewController presentViewController:gcViewController animated:YES completion:nil];
}
I don't have problems presenting the window, however I cannot manage to let users close it. I implemented the method gameCenterViewControllerDidFinish however the function is not called at all. What do I have to do so users can close the leaderboard?
I fixed by adding the line gcViewController.gameCenterDelegate = _spriteViewController;
-(void)showLeaderboardAndAchievements:(BOOL)shouldShowLeaderboard{
GKGameCenterViewController *gcViewController = [[GKGameCenterViewController alloc] init];
gcViewController.viewState = GKGameCenterViewControllerStateLeaderboards;
gcViewController.leaderboardIdentifier = _leaderboardIdentifier;
gcViewController.gameCenterDelegate = _spriteViewController; // er
// _spriteViewController references an UIViewController
[_spriteViewController presentViewController:gcViewController animated:YES completion:nil];
}
While it worked I got the warning:
Assigning to 'id<GKGameCenterControllerDelegate>' from incompatible type 'UIViewController *__weak'
For anyone in this situation the problem was simply that _spriteViewController was of typeUIViewController, changing that to my custom class fixed the issue. You can also declare the property referencing the ViewController like this:
UIViewController <GKGameCenterControllerDelegate> *_spriteViewController;
Related
I'm trying to detect when a UIPopoverController gets dismissed, but it seems not to work properly, the methods are not getting called. This is my code for presenting the UIPopoverController:
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
imagePickerController.delegate = self;
UIPopoverController *popoverVC = [[UIPopoverController alloc] initWithContentViewController:imagePickerController];
popoverVC.delegate = self;
[popoverVC presentPopoverFromRect:CGRectMake(self.frame.origin.x,self.frame.origin.y, self.frame.size.width, self.frame.size.height) inView:[home view] permittedArrowDirections:0 animated:YES];
I'm trying to fire this method, but is not getting called:
- (void) popoverControllerDidDismissPopover:(UIPopoverController *) popoverController;
And I'm indeed adding the delegate to the header:
#interface Map : UIView <UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIGestureRecognizerDelegate, UIScrollViewDelegate,UIPopoverControllerDelegate>
I don't know what I'm missing out, is not working at all.
You probably already solved it, but I just faced the same problem. I'm holding a instance of UIPopoverController in my Viewcontroller and had it this way:
self.popover.delegate = self;
self.popover = [[UIPopoverController alloc] initWithContentViewController:wgtvc];
of course this doesn't work because I'm initializing the UIPopoverController AFTER setting the delegate, which overrides the delegate setting. So the correct way is to FIRST initialize the UIPopovercontroller and THEN setting the delegate
self.popover = [[UIPopoverController alloc] initWithContentViewController:wgtvc];
self.popover.delegate = self;
Maybe you are reinitializing your UIPopoverController somewhere - just set the delegate again after reinitializing.
Hpoe this helps.
So, I figured out how to make it work.
I made a "global" variable for the class.
self.popoverVC = [[UIPopoverController alloc] initWithContentViewController:imagePickerController];
self.popoverVC.delegate = self;
Because it's an UIImagePickerController inside a UIPopoverController, I needed to listen to the UIImagePickerController for dismission too and combine both methods:
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker;
- (void) popoverControllerDidDismissPopover:(UIPopoverController *) popoverController
So I can now detect when the UIPopoverController gets dismissed.
The following documentation has the sample code below:
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/GameKit_Guide/LeaderBoards/LeaderBoards.html#//apple_ref/doc/uid/TP40008304-CH6-SW9
Listing 4-9 Displaying the leaderboard page of the Game Center user
interface
- (void) showLeaderboard: (NSString*) leaderboardID
{
GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
if (gameCenterController != nil)
{
gameCenterController.gameCenterDelegate = self;
gameCenterController.viewState = GKGameCenterViewControllerStateLeaderboards;
gameCenterController.leaderboardTimeScope = GKLeaderboardTimeScopeToday;
gameCenterController.leaderboardCategory = leaderboardID;
[self presentViewController: gameCenterController animated: YES completion:nil];
}
}
When do you call "release" on the GKGameCenterViewController ? Should it be only after the view controller is dismissed, or can you call it at the end of this method ? Or should one just call autorelease ?
The project uses Automatic Reference Counting, therefore you do not need to explicitly declare release or dealloc. For reference, it is deallocated after the view leaves the view hierarchy.
I can't find any documentation to back this up (because all of Apple's documentation now assumes ARC), but my recollection from the pre-ARC days is that you release a modal view controller once you present it. I'm reasonably sure the presenting view controller will take a strong reference to the presented view controller. So:
- (void) showLeaderboard: (NSString*) leaderboardID
{
GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
if (gameCenterController != nil)
{
gameCenterController.gameCenterDelegate = self;
gameCenterController.viewState = GKGameCenterViewControllerStateLeaderboards;
gameCenterController.leaderboardTimeScope = GKLeaderboardTimeScopeToday;
gameCenterController.leaderboardCategory = leaderboardID;
[self presentViewController: gameCenterController animated: YES completion:nil];
[gameCenterController release];
}
}
A better answer might be, "use ARC". :) Seriously, ARC rules.
I'm using Apple's code to show a GKGameCenterViewController:
GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
if (gameCenterController != nil) {
gameCenterController.gameCenterDelegate = self;
[self presentViewController:gameCenterController animated:YES completion:nil];
}
This is the text describing the code above:
Game Center UI is Displayed by Your View Controller (iOS)
The convention used by Game Kit is for one of your view controllers to present the Game Kit view controller. Your view controller acts as a delegate to the view controller it presents so that it can be informed when the player is finished looking at the presented screen. Listing 2-1 shows most common use of this pattern, which is to show the Game Center user interface. The Game Center view controller displays many different pieces of Game Center content, so most games should offer a button that brings the player to this screen, even if the game also shows Game Center content using a custom user interface.
When I use the recommended code I get to this screen (GameCenter Challenges), which is not what I want:
I have also tried this code:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"gamecenter:"]];
By using that code, I get to the screen I expected to display:
Do I misunderstand something or am I doing something wrong? Shouldn't the first piece of code bring me to the main menu? Why won't it show the leaderboards?
UPDATE
I implemented viewState as suggested by phix23:
GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
if (gameCenterController != nil) {
gameCenterController.gameCenterDelegate = self;
gameCenterController.viewState = GKGameCenterViewControllerStateLeaderboards;
[self presentViewController:gameCenterController animated:YES completion:nil];
}
But, it still displays the same Challenges screen, despite the fact that I want /try to display the Leaderboards screen.
The GKGameCenterViewController which is available since iOS 6 can show the leaderboards, achievements and challenges of your game center enabled application.
You can change the initial view by setting the viewState of the GKGameCenterViewController. If you don't set this property it will show the default view, which is the challenges view in your case. I guess you don't have setup any leaderboards or achievements so there is nothing to be shown.
Try using this code:
-(void)showGameCentersDefaultPage {
GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
if (gameCenterController != nil) {
gameCenterController.gameCenterDelegate = self;
gameCenterController.viewState = GKGameCenterViewControllerStateDefault;
[self presentViewController:gameCenterController animated:YES completion:nil];
}
}
If you want to begin with a specific type of GameCenter Leaderboard you can call the following method with your leaderboardID
- (void)showLeaderboard:(NSString*)leaderboardID {
GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
if (gameCenterController != nil) {
gameCenterController.gameCenterDelegate = self;
//The next three lines are the lines of interest...
gameCenterController.viewState = GKGameCenterViewControllerStateDefault;
gameCenterController.leaderboardTimeScope = GKLeaderboardTimeScopeToday;
gameCenterController.leaderboardCategory = leaderboardID;
[self presentViewController:gameCenterController animated:YES completion:nil];
}
}
For iOS 7.0, I use the following:
Display Leaderboard:
- (void)displayLeaderboard:(UIViewController *)viewController
{
GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
if (gameCenterController != nil) {
gameCenterController.gameCenterDelegate = self;
gameCenterController.viewState = GKGameCenterViewControllerStateLeaderboards;
[viewController presentViewController:gameCenterController animated:YES completion:nil];
}
}
Display Achievements:
- (void)displayAchievements:(UIViewController *)viewController
{
GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
if (gameCenterController != nil) {
gameCenterController.gameCenterDelegate = self;
gameCenterController.viewState = GKGameCenterViewControllerStateAchievements;
[viewController presentViewController:gameCenterController animated:YES completion:nil];
}
}
Note that the view controller trying to use these functions will need to pass itself (i.e. viewController param must be set to some active view controller).
Hope this helps.
Use GKGameCenterViewController and set the view state:
//Create a leaderboard view controller
GKGameCenterViewController *leaderboardViewController = [[GKLeaderboardViewController alloc] init];
leaderboardViewController.viewState = GKGameCenterViewControllerStateLeaderboards;
//Set the time scope (ex. All Time, This Week, Today) and the leaderboard ID
leaderboardViewController.timeScope = GKLeaderboardTimeScopeAllTime;
leaderboardViewController.leaderboardCategory = leaderboardID;
//Set the delegate so we can handle various actions including dismissal
leaderboardViewController.leaderboardDelegate = self;
//Present the view controller
[self presentViewController:leaderboardViewController animated:YES completion:nil];
This code will present a view controller that displays all of your games leaderboards (or the rankings if there is only one). You can also set properties such as which leaderboard to show, the time scope, delegate, etc. Also note that you can do a similar thing with achievements using the GKAchievementViewController.
The code that you provided in your question,
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"gamecenter:"]];
Launches the GameCenter app. This means that iOS will exit your app and switch to GameCenter. This could be confusing to the user. You should also avoid making the user leave your app. Instead, use the GKViewControllers which are presented modally inside of your app.
I have a normal UIPopoverController which is made in this way:
-(IBAction)btKBIs_click:(id)sender
{
if(kbiPopOver != NULL)
[kbiPopOver dismissPopoverAnimated:YES];
KBIViewController *kbiViewController = [[KBIViewController alloc]initWithNibName:#"KBIViewController" bundle:nil CurrentUser:currentUser];
kbiViewController.currentStatus = FIRST;
kbiViewController.firstlist = [currentUser getDescriptions];
kbiViewController.mapViewController =self;
UINavigationController* kbiNavController = [[UINavigationController alloc] initWithRootViewController:kbiViewController];
kbiPopOver = [[UIPopoverController alloc] initWithContentViewController:kbiNavController];
kbiPopOver.delegate = self;
kbiViewController.kbiPopOver = kbiPopOver;
[kbiPopOver presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionUp animated:true];
}
Inside the class KBIViewController I'm calling:
[self.kbiPopOver dismissPopoverAnimated:YES];
[self.kbiPopOver.delegate popoverControllerDidDismissPopover:self.kbiPopOver];
To dismiss it but it does not work. Why?
Did you add the <UIPopOverControllerDelegate> in your class interface and did you implement the – popoverControllerDidDismissPopover: method? Just call the dismissPopoverAnimated: explicitly in your code or in the delgate method, when you tap outside the popover. Remove that [self.kbiPopOver.delegate popoverControllerDidDismissPopover:self.kbiPopOver]; from your code.
I assure you that I did look for an answer in SO for my question but none of them were helpful. Here I got a simple code that should present a UIImagePickerController within a UIPopoverController:
-(void)takePicture:(id)sender{
UIImagePickerController *picker=[[UIImagePickerController alloc] init];
picker.delegate=self;
picker.sourceType=UIImagePickerControllerSourceTypeCamera;
picker.allowsEditing=YES;
UIPopoverController *poc=[[UIPopoverController alloc]
initWithContentViewController:picker];
[poc presentPopoverFromBarButtonItem:bbItem
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:NO];
}
Now, even from the first time I get [UIPopoveController dealloc] reached while... error and the program crashes. I'm not doing any retain,relase or autoreleases as per ARC. Is there any special consideration with UIPopoverControllers when benefitting from ARC?
UIPopoverControllers should always be held in an instance variable. It is a good practice to create a strong property for it.
UPDATE:
As of iOS 8 you should be using UIPopoverPresentationController. Then you don't need to keep a reference to the popover because it is managed by the presentation controller.
Code example (works both on iPhone and iPad):
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.allowsEditing = YES;
picker.modalPresentationStyle = UIModalPresentationPopover;
UIPopoverPresentationController* popoverPC = picker.popoverPresentationController;
popoverPC.barButtonItem = bbItem;
popoverPC.permittedArrowDirections = UIPopoverArrowDirectionAny;
[self presentViewController:picker animated:YES completion:nil];
When the function exits there are no other reference to the popover controller, so it's deallocated too early.
Try adding it as a member of your class instead.
Tim
Adding what #phix23 answered, create *poc property like this:
#property (nonatomic, retain) IBOutlet UIPopoverController *poc;
and then change
UIPopoverController *poc = [[UIPopoverController alloc]
initWithContentViewController:picker];
for
self.poc = [[UIPopoverController alloc]
initWithContentViewController:picker];