GKScore challengeComposeControllerWithPlayers not showing view - ios7

Here's my code:
GKScore *scoreObj = [[[GKScore alloc] initWithCategory:category] autorelease];
scoreObj.value = playerScore;
[scoreObj
challengeComposeControllerWithPlayers:playerIDs
message:msg
completionHandler:^(UIViewController *composeController,
BOOL didIssueChallenge,
NSArray *sentPlayerIDs) {
// I don't really care what you do
}];
When I run the code, I get no overlay at all, and no error messages in the log.
Currently, playerIDs is an NSMutableArray that's empty. I don't know if that's related, but I don't want to have to pre-populate a list - that's what this native UI is suppoed to do for me, right? mgs is an empty string.

First of all in iOS 7 initWithCategory: is deprecated. Use initWithLeaderboardIdentifier: instead. As the documentation says:
Provides a challenge compose view controller with pre-selected player identifiers and a preformatted, player-editable message.
So save a pointer to this view controller, present it from your current view controller and dismiss it from within the completion handler.
Assuming you're within the implementation of your current viewController do the following:
GKScore *scoreObj = [[[GKScore alloc] initWithLeaderboardIdentifier:category] autorelease];
scoreObj.value = playerScore;
UIViewController *vc = [scoreObj challengeComposeControllerWithPlayers:playerIDs
message:msg
completionHandler:^(UIViewController *composeController,
BOOL didIssueChallenge,
NSArray *sentPlayerIDs) {
[composeController.presentingViewController dismissViewControllerAnimated: YES completion: nil];
// I don't really care what you do
}];
[self presentViewController: vc animated: YES completion: nil];
This should displayed the viewController as intended.

Related

How do UIAlertView or UIActionSheet handle retaining/releasing themselves under ARC?

I want to create a similar class to UIAlertView which doesn't require a strong ivar.
For example, with UIAlertView, I can do the following in one of my UIViewController's methods:
UIAlertView *alertView = [[UIActionSheet alloc] initWithTitle:nil
message:#"Foo"
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:nil];
[alertView show];
... and the actionSheet will not be dealloced until it is no longer visible.
If I were to try to do the same thing:
MYAlertView *myAlertView = [[MYAlertView alloc] initWithMessage:#"Foo"];
[myAlertView show];
... the myAlertView instance will automatically be dealloced at the end of the current method I am in (e.g. right after the [myAlertView show] line).
What is the proper way to prevent this from happening without having to declare myView as a strong property on my UIViewController? (I.e. I want myView to be a local variable, not an instance variable, and I would like the MYAlertView instance to be in charge of its own lifecycle rather than my UIViewController controlling its lifecycle.)
Update: MYAlertView inherits from NSObject, so it cannot be added to the Views hierarchy.
UIAlertView creates a UIWindow, which it retains. The alert view then adds itself as a subview of the window, so the window retains the alert view. Thus it creates a retain cycle which keeps both it and its window alive. UIActionSheet works the same way.
If you need your object to stay around, and nothing else will retain it, it's fine for it to retain itself. You need to make sure you have a well-defined way to make it release itself when it's no longer needed. For example, if it's managing a window, then it should release itself when it takes the window off the screen.
If you add it as a subview of another view it will be retained. When the user selects and action or dismisses it, then it should call self removeFromSuperview as it's last act.
I've done my own AlertView with a little trick.
Just retain the object himself and release it on action. With this, you can call your custom alert vies as native one.
#import "BubbleAlertView.h"
#import <QuartzCore/QuartzCore.h>
#interface BubbleAlertView ()
...
#property (nonatomic, strong) BubbleAlertView *alertView;
...
#end
#implementation BubbleAlertView
...
- (id)initWithTitle:(NSString*)title message:(NSString*)message delegate:(id)delegate cancelButtonTitle:(NSString*)cancelButtonTitle okButtonTitle:(NSString*) okButtonTitle
{
self = [super init];
if (self)
{
// Custom initialization
self.alertView = self; // retain myself
//More init stuff
}
return self;
}
...
//SHOW METHOD
- (void)show
{
// We need to add it to the window, which we can get from the delegate
id appDelegate = [[UIApplication sharedApplication] delegate];
UIWindow *window = [appDelegate window];
[window addSubview:self.view];
// Make sure the alert covers the whole window
self.view.frame = window.frame;
self.view.center = window.center;
}
- (IBAction)btPressed:(id)sender
{
//Actions done
[UIView animateWithDuration:0.4f animations:^{
self.vContent.alpha = 0.f;
} completion:^(BOOL finished) {
[self.view removeFromSuperview];
self.alertView = nil; // deallocate myself
}];
}
You need to retain it somehow until it is released.
I do not really understand why you cannot implement it as subclass of UIView. Then you could use the view hierarchy as the keeper of a strong reference (retain +1). But you will have good reasons for not doing so.
If you don't have such a thing then I would use an NSMutableArray as class varialbe (meaning statc). Just declare it in the #interface block and initialize it with nil:
#interface
static NSMutableArray _allMyAlerts = nil;
provide an accessor.
-(NSMutableArray *) allMyAlerts {
if (_allMyAlerts == nil) {
_allMyAlerts = [[NSMutableArray alloc] init];
}
return _allMyAlerts
}
Within the init method do the following:
- (id) init {
self = [super init];
if (self) {
[[self allMyAlerts] addObject:self];
}
}
You will invode some method when the alert is dismissed.
- (void) dismissAlert {
// Do your stuff here an then remove it from the array.
[[self allMyAlerts] removeObject:self];
}
You may want to add some stuff to make it mutli threading save, which it is not. I just want to give an example that explains the concept.
allMyAlert could be an NSMutableSet as well. No need for an array as far as I can see. Adding the object to an array or set will add 1 to the retain count and removing it will reduce it by 1.

how to use presentedViewController when testing for modal launch

I'm unit testing one of my view controllers and wanted to confirm that when a logic branch is hit, the modal view controller that I pop up with present:
viewDidLoad
MWLoginViewController *loginController = [[MWLoginViewController alloc] init];
[self presentModalViewController:loginController animated:YES];
The code is hit in the debugger when I put a breakpoint.
Now how do I test to see if this was launched?
I am trying:
BOOL wasLoginViewController = [[mainVcSUT presentedViewController] isMemberOfClass: [MWLoginViewController class]];
[Assert isTrue:wasLoginViewController];
NSLog(#"presented VC was: %#", [[mainVcSUT presentedViewController] class]);
The NSLog is telling me view controller was null.
MWLoginViewController *loginController = [[MWLoginViewController alloc] init];
you are creating just an object , does it have it`s own view ? create viewcontroller either initwithnibname or init from storyboard , then check it
plus , you should not load another view from viewDidLoad: method ...

How to not push a segue into the Nav Controller Stack?

I would like to know what kind of trick to use for "not pushing" a view controller into the navigation controller stack (iOS)
I have this :
If user is not logged, show view A then show B
If user is logged, show B
As I am using the storyboard, I used a performSegue if the user is logged so he goes directly to B. But with this method, the Navigation Controller gets a push of view A in the stack.
I was thinking of poping out a level of the stack in some void (but I don't know how to do this).
I was also thinking of not pushing the view into the nav controller stack (but I don't know how to do this).
Thanks
Update :
I tried this :
//The view B
TabBarMain* mainViewController = [[TabBarMain alloc] init];
//If already logged in
if([username length] == 0)
{
NSArray *viewControllers = [NSArray arrayWithObject:mainViewController];
[self.navigationController setViewControllers:viewControllers animated:NO];
}
The problem of this code is that it shows me a black screen (doesn't crash). It seems that I need to init something and I have nothing in my TabBarMain.m, I don't know what to write in there. This TabBarMain is linked to the Tab Bar Controller of the Storyboard.
Is there no other way ?
Try this on for size in your rootViewController's viewDidLoad.
- (void)viewDidLoad
{
NSArray *viewControllers
if (logged) {
NSArray *viewControllers = [NSArray arrayWithObject:viewControllerB];
} else {
NSArray *viewControllers = [NSArray arrayWithObject:viewControllerA];
}
[self.navigationController setViewControllers:viewControllers animated:NO];
}
Since your viewController is linked in Storyboard and not instantiated in code you need to instantiate it from the storyboard not your empty code. Make sure the identifier matches the identifier for your ViewController in your storyboard.
TabBarMain *mainViewController = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:NULL] instantiateViewControllerWithIdentifier:#"tabBarMain"];

How can i make my application allways start with the second view?

I have a Web Service application and it has alogin view. I want to make my application's login view come when the first time app is loaded(installed) and after that it must allways start with a second view. How can i make it? In this link there are some solutions but i think this isn't what i'm looking for. Since mine is a web servise, mean the content of the second view(which i want to be pushed allways) is fetched from a server(i use NSJSONSerialization class for this work)
I would do the login view as a modal view which is only presented when needed.
Edit:
This is VERY brief: (I assume that you are using ARC.)
In AppDelegate:
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController: mySecondViewController];
if (![self isUserLoggedIn]) {
MyLogInViewController *logInViewController = [[MyLogInViewController alloc] init];
[self presentModalViewController: MyLogInViewController animated: YES];
}
[[self window] setRootViewController: [self navigationController]];
and in logInViewController:
- (void)logInSuccessful {
[self dismissModalViewControllerAnimated: YES];
}

UIViewController and UITableViewController, how to change self.view then back again?

I dont want to add a sub view, but instead change the "self.view" to another view eg (A warning view) then after the user suppresses the warning I would like to switch back. When ever i try to switch back to the original view i just get a blank screen for reasons i cant understand.
Here is what i currently have in one of my UITableViewControllers
//Show warning view controller
self.warningViewControler = [[[WarningViewController alloc] init] autorelease];
self.view = self.warningViewController.view;
//Then later
self.view = self.tableView; //<< Dosnt work
If you want to change your view, and if the original view is defined/linked into XCode, you must retain it before changing self.view to another view. If not, the original view is released and using it back can cause bad things to happen.
Warning :
self.warningViewControler = [[[WarningViewController alloc] init] autorelease];
self.view = self.warningViewController.view
is a bad bad call. Because you autorelease the controller but you use its view. So you get a view retained with a released controller after some time. Retain the controller and release it yourself when its view is not needed anymore.
Here's the better way to do what I think you're trying to do:
WarningViewController *warningViewController = [[WarningViewController alloc] initWithNibName:#"theRightNiborNil" bundle:nil];
[self presentModalViewController:warningViewController animated:YES];
// or if you don't need to support iOS4 any more:
[self presentViewController:warningViewController animated:YES completion:nil]
// and if you aren't using ARC yet, then [warningViewController release];
Then in your WarningViewController you want some action that calls:
[self dismissModalViewControllerAnimated:YES];
// or again if this is iOS5..
[self dismissModalViewControllerAnimated:YES completion:nil];
Hope that helps.