How to accept an invitation in Game Center - iphone

I'm trying to implement invitations with Game Center and there's one thing that i don't understand. Ok, i've sent an invitation from one device to another. Then i have an UIAlertView on receiver which asks me i would like to accept or decline the invitation. when i accept it it is handled like this:
[GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite)
{
// Insert application-specific code here to clean up any games in progress.
if (acceptedInvite)
{
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithInvite:acceptedInvite] autorelease];
mmvc.matchmakerDelegate = self;
[presentingViewController presentModalViewController:mmvc animated:YES];
}
else if (playersToInvite)
{
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = 2;
request.maxPlayers = 4;
request.playersToInvite = playersToInvite;
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
mmvc.matchmakerDelegate = self;
[presentingViewController presentModalViewController:mmvc animated:YES];
}
};
Well, that's great, but what next? the sender device is obviously waiting for some standard type of response, cause it also shows an alert telling me that there's some invitations not answered yet if i tap "Play now".
So how do i accept an invitation? What kind of data (and how) should i send back? And what exactly should i do on the receiver's side? Should game start instantly after tapping "Accept" or i should dismiss the AlertView first and then tap "Play now"?
Ray Wenderlich's tutorial says that i should choose second way but when dismiss the alert and tap "Play now" it turns out the sender device is still waiting for response and is not aware that i have already accepted the invitation. if i tap "Play now" at this moment then, as i said above, it shows an alert which says that the application is waiting for the response. So if you've ever done that then please explain me what should i do. Thanks!

I register for invites as soon as the game is loaded and call the
delegate when an invite is received
So inviteReceived calls the match maker for dismissing the game
center controller and creating the match
And finally, when the match is being found, the method
connectionStatusChanged takes care of presenting all the game's
views and players and stuff
Here is the code:
I register for invites as soon as the game is loaded and call the delegate when an invite is received:
- (void)registerInvites {
[GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite) {
self.pendingInvite = acceptedInvite;
self.pendingPlayersToInvite = playersToInvite;
[delegate inviteReceived];
};
}
So inviteReceived calls the match maker for dismissing the game center controller and creating the match:
- (void)inviteReceived {
[[GCMultiplayerHelper sharedInstance] findMatchWithMinPlayers:2 maxPlayers:2 viewController:(UIViewController*)[self.superview nextResponder] delegate:self];
}
- (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers viewController:(UIViewController *)viewController delegate:(id<GCMultiplayerHelperDelegate>)theDelegate {
if (!gameCenterAvailable) return;
matchStarted = NO;
self.match = nil;
self.presentingViewController = viewController;
delegate = theDelegate;
[presentingViewController dismissModalViewControllerAnimated:YES];
GKMatchmakerViewController *mmvc;
if (pendingInvite != nil) {
mmvc = [[[GKMatchmakerViewController alloc] initWithInvite:pendingInvite] autorelease];
} else {
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = minPlayers;
request.maxPlayers = maxPlayers;
request.playersToInvite = pendingPlayersToInvite;
mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
}
mmvc.matchmakerDelegate = self;
[presentingViewController presentModalViewController:mmvc animated:YES];
self.pendingInvite = nil;
self.pendingPlayersToInvite = nil;
}
And finally, when the match is been found, the method connectionStatusChanged takes care of presenting all the game's views, players and starting the match:
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFindMatch:(GKMatch *)theMatch
{
self.match = theMatch;
match.delegate = self;
if (!matchStarted && match.expectedPlayerCount == 0) {
NSLog(#"Ready to start match! - didFindMatch");
[presentingViewController dismissModalViewControllerAnimated:YES];
[self.delegate connectionStatusChanged:CONNECTIONSUCCESS];
}
}

I've successfully implemented an online game center match following Ray's tutorials. The answer to you question is: You don't have to send anything to the inviting device. When you call the line:GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];, the matchMakerVController handles the connection . However, you should write an invitation handler ASAP, preferably in the authentication changed method. See mine:
-(void) authenticationChanged {
if ([GKLocalPlayer localPlayer].isAuthenticated && !userAuthenticated) {
NSLog(#"Authentication changed: player authenticated.");
userAuthenticated = TRUE;
[self sendUnsentScores];
[GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite){
NSLog(#"Invite");
if([AppDelegate mainMenuController].presentedViewController!=nil) {
[[AppDelegate mainMenuController] dismissViewControllerAnimated:NO completion:^{
}];
} // if we're not on the main menu, or another game is going on.
// this would be easier to do if you were using a navigation controller
// where you'd just push the multiplayer menu etc.
self.pendingInvite = acceptedInvite;
self.pendingPlayersToInvite = playersToInvite;
[[AppDelegate mainMenuController] presentViewController:[AppDelegate mainMenuController].multiGameMenu animated:NO completion:^{ // push the multiplayer menu
[[AppDelegate mainMenuController].multiGameMenu duel:nil];
}];
};
}
and here is the duel method if you're interested. Very messy code but deal with it :)
- (IBAction)duel:(id)sender {
NSLog(#"duel");
if (presentingMenu.multiGameController==nil || presentingMenu.multiGame.gameInProgress==NO) {
presentingMenu.multiGame=nil;
presentingMenu.multiGameController=nil;
MultiViewController *mvc = [[MultiViewController alloc] init]; //create game VC
presentingMenu.multiGameController = mvc; //presenting menu is just the main menu VC
// it holds this menu, and the game
// objects.
}
if (presentingMenu.multiGame == nil) {
presentingMenu.multiGame = [[MultiGame alloc] // similarly create the game object
initWithViewController:presentingMenu.multiGameController];
//they both have pointers to each other (A loose, bad MVC).
presentingMenu.multiGameController.game = presentingMenu.multiGame;
[presentingMenu.multiGame startGame];
}
if (presentingMenu.multiGameController.gameState==0) { //new game
presentingMenu.multiGameController.game = presentingMenu.multiGame;
[[GCHelper sharedInstance] findMatchWithMinPlayers:2 maxPlayers:2 viewController:self delegate:presentingMenu.multiGame]; // the GC magic happens here - it know about the invite.
} else {
[self presentViewController:presentingMenu.multiGameController animated:YES completion:^{
}];
}
}

inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite) is deprecated now. See the new way to register for invite notifications.
GKMatchMaker invite handler deprecated

Related

Game Center Invitation

I am using Game Kit and I want to submit a score on Game Center. I did that but now I want to invite friends. How do I invite my friends and how do I see my friend's score? This code is for show leaderboard:
- (IBAction) showLeaderboard
{
GKLeaderboardViewController *leaderboardController = [[GKLeaderboardViewController alloc] init];
if (leaderboardController != NULL)
{
leaderboardController.category = self.currentLeaderBoard;
leaderboardController.timeScope = GKLeaderboardTimeScopeWeek;
leaderboardController.leaderboardDelegate = self;
[self presentModalViewController: leaderboardController animated: YES];
}
}
The general functions needed for friend interaction in Game Center can be found in Working with Players in Game Center.
There is even a section on inviting friends under Allowing the Local Player to Invite Other Players to Be Friends:
- (void) inviteFriends: (NSArray*) identifiers
{
GKFriendRequestComposeViewController *friendRequestViewController = [[GKFriendRequestComposeViewController alloc] init];
friendRequestViewController.composeViewDelegate = self;
if (identifiers)
{
[friendRequestViewController addRecipientsWithPlayerIDs: identifiers];
}
[self presentViewController: friendRequestViewController animated: YES completion:nil];
[friendRequestViewController release];
}

iOS5 problems with old applications

I need advice on what to do.
I have several applications on App store and they are all tested and fully functional for all previous iOS's. But now when I update my device to iOS5 some of them began to crash unexpectedly on alert view item that ask for connecting to internet and show map with current location.
I am not that experienced in development so need some advice what to do? I supposed that with final version of iOS5 these things will be fixed by itself or not.
Thanks.
OK I have torned zombies and found method that makes problems.
- (void) alertView: (UIAlertView*) alertView didDismissWithButtonIndex:(NSInteger)buttonIndex{
NSLog(#" Button PRESSED: %d", buttonIndex);
[alertView release];
if (buttonIndex == 1) {
BOOL noConnectionAvailable = NO;
BOOL hasParentalLimit = NO;
switch (lastSelectedItem.itemType) {
case RestaurantItemTypeAddress : {
if ([NetworkHelper connectedToNetwork] == YES) {
AddressController *mapController = [[AddressController alloc] initWithNibName:#"AddressController" bundle:nil restaurant:restaurant];
mapController.title = restaurant.res_title;
[self.navigationController pushViewController:mapController animated:YES];
[mapController release];
} else
noConnectionAvailable = YES;
break;
}
case RestaurantItemTypeReservationEmail : {
if ([NetworkHelper connectedToNetwork] == YES) {
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
[picker setSubject:#"Reservation"];
// Set up recipients
NSArray *toRecipients = [NSArray arrayWithObject:lastSelectedItem.itemTextContent];
[picker setToRecipients:toRecipients];
// Fill out the email body text
NSString *emailBody = #"";
[picker setMessageBody:emailBody isHTML:NO];
[self presentModalViewController:picker animated:YES];
[picker release];
[self.tableView deselectRowAtIndexPath:lastSelectedIndexPath animated:YES];
} else
noConnectionAvailable = YES;
break;
}
case RestaurantItemTypeReservationForm : {
if ([NetworkHelper connectedToNetwork] == YES) {
if ([NetworkHelper canOpenUrl:lastSelectedItem.itemTextContent]) {
WebViewController *wvc = [[WebViewController alloc] initWithNibName:#"WebViewController" bundle:nil urlStr:lastSelectedItem.itemTextContent];
wvc.title = restaurant.res_title;
[self.navigationController pushViewController:wvc animated:YES];
[wvc release];
} else hasParentalLimit = YES;
} else
noConnectionAvailable = YES;
break;
}
case RestaurantItemTypeWeb : {
if ([NetworkHelper connectedToNetwork] == YES) {
if ([NetworkHelper canOpenUrl:lastSelectedItem.itemTextContent]) {
WebViewController *wvc = [[WebViewController alloc] initWithNibName:#"WebViewController" bundle:nil urlStr:lastSelectedItem.itemText];
wvc.title = restaurant.res_title;
[self.navigationController pushViewController:wvc animated:YES];
[wvc release];
} else hasParentalLimit = YES;
} else
noConnectionAvailable = YES;
break;
}
}
if (noConnectionAvailable == YES) {
UIAlertView* newAlert = [[UIAlertView alloc] initWithTitle:#"Jesolo Official Guide"
message:#"Nessuna connessione disponibile."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[newAlert show];
}
if (hasParentalLimit == YES) {
UIAlertView* newAlert = [[UIAlertView alloc] initWithTitle:#"Jesolo Official Guide"
message:#"Navigazione su Web non consentita."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[newAlert show];
}
}
[self.tableView deselectRowAtIndexPath:lastSelectedIndexPath animated:NO];
}
The log is different on iOS4 and iOS5.
On iOS5 it said:
2011-11-04 16:26:28.550 Jesolo-EN[5693:207] Button PRESSED: 1
2011-11-04 16:26:28.776 Jesolo-EN[5693:207] *** -[NSIndexPath isEqual:]: message sent to deallocated instance 0xe6b6fc0
sharedlibrary apply-load-rules all
Current language: auto; currently objective-c
warning: Attempting to create USE_BLOCK_IN_FRAME variable with block that isn't in the frame.
(gdb)
and on iOS4 said:
2011-11-04 16:28:08.087 Jesolo-EN[5859:207] Button PRESSED: 1
2011-11-04 16:28:08.162 Jesolo-EN[5859:207] *** -[UIAlertView release]: message sent to deallocated instance 0x78c4940
sharedlibrary apply-load-rules all
Current language: auto; currently objective-c
(gdb)
I understand that i release data before i use them but when I run app without enabled zombies it work fine on iOS4 and on iOS5 it run one time and then 10 times crash.
I am finding that references to ivars that previously worked are now needing to be referenced as self.ivar. Perhaps your reference to managedObjectContext should instead be self.managedObjectContext?
I am not very much sure about this but you can try this also..
Click on your **project**->go to **info**-> go to **build** section-> in that go to **Deployment** section->then **IOS deployment** section choose the **deployment target to IOS 5** or latest and save and run...
Hope it may help you... :)
OK I manage to find solution.
Somehow iOS5 detect some release of objects that doest'n exist in iOS4 and I tried to find them but gave up.
Instead of that I turn on ARC and comment all release and dealloc and my app is doing really fine. I get impression that now it works even faster. Hope this will help somebody...

How to post tweet from iPhone without PIN using SA_OAuthTwitterEngine?

I am developing a iPhone application which sends tweets to twitter. For this I am using SA_OAuthTwitterEngine + MGTwitterEngine classes.
I register applicaiton to www.twitter.com/apps and pass Consumer key and Consumer secret to controller my code is this.
if(!_engine){
_engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate:self];
_engine.consumerKey = slPcFUjUh5y1hex0zvEhPg;
_engine.consumerSecret = u6ydovMdP9yeiVqDukVhIzZPgJR9XDPUwfxymzNs;
}
UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine:_engine delegate:self];
if (controller){
[self presentModalViewController: controller animated: YES];
intTwitterFlag = 1;
}
Previously on twitter.com/apps I select Application type = client and my application will generate PIN and accessToken. But when i change my Application type = Browser it cannot generate PIN and accessToken.
Previously when application type is client i am giving user name and password and then control return to my application from the webview but now after entering user name and password it cannot dismissModalViewController but showing Select and Copy the PIN.
Thank you for your time and any help you can give me!
Here it is:just replace the method in SA_OAuthTwitterController.m:
- (void) webViewDidFinishLoad: (UIWebView *) webView {
_loading = NO;
//[self performInjection];
if (_firstLoad) {
[_webView performSelector: #selector(stringByEvaluatingJavaScriptFromString:) withObject: #"window.scrollBy(0,200)" afterDelay: 0];
_firstLoad = NO;
} else {
/*
NSString *authPin = [self locateAuthPinInWebView: webView];
NSLog(#"authPin: %#", authPin);
if (authPin.length) {
[self gotPin: authPin];
return;
}
NSString *formCount = [webView stringByEvaluatingJavaScriptFromString: #"document.forms.length"];
if ([formCount isEqualToString: #"0"]) {
[self showPinCopyPrompt];
}*/
//*****************************************************
// This is to bypass the pin requirement
// in case the call back URL is set in Twitter settings
//*****************************************************
[_engine requestAccessToken];
if ([_delegate respondsToSelector: #selector(OAuthTwitterController:authenticatedWithUsername:)])
{
[_delegate OAuthTwitterController: self authenticatedWithUsername: _engine.username];
}
[self performSelector: #selector(dismissModalViewControllerAnimated:) withObject: (id) kCFBooleanTrue afterDelay: 1.0];
//[self dismissModalViewControllerAnimated:YES];
}
[UIView beginAnimations: nil context: nil];
_blockerView.alpha = 0.0;
[UIView commitAnimations];
if ([_webView isLoading]) {
_webView.alpha = 0.0;
} else {
_webView.alpha = 1.0;
}
}
I too faced the same issue, and i just removed callback url from the twitter app settings. For my surprise login proceeds without any issue.
The replacement for this method
(void) webViewDidFinishLoad: (UIWebView *) webView
in the class SA_OAuthTwitterController.m works well.
Its better to use xAuth route for mobile apps
http://dev.twitter.com/pages/xauth
check XAuthTwitterEngine which implements xauth for the MGTwitterEngine

UIImagePicker view not removing itself after first use

I have an app that is adding a view that is used to call UIImagePickerController. When the use hits the add image button, the following code executes:
'- (IBAction)addPhoto:(id)sender {
// Call background tap in case any keyboards are still up
[self backgroundTap:sender];
if (jpegData) {
// If we already chose an image, don't allow to choose another.
// Have to cancel out and come back!
return;
}
// Shows the photo picker so the user can take or select an image
photoPickerViewController = [[PhotoPickerViewController alloc] initWithNibName:#"PhotoPicker" bundle:nil];
photoPickerViewController.delegate = self;
// Add it to the subview - it will auto animate in/out
[self.view addSubview:photoPickerViewController.view];
}
This presents the user with a view that I created that has 3 buttons: Take photo, choose existing photo, and cancel. Cancel just cancels back to the main view. If take photo or choose existing is called, this code is executed:
'- (IBAction)choosePhoto:(id)sender {
// Show an image picker to allow the user to choose a new photo.
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
if((UIButton*)sender == chooseExistingButton) {
imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
} else {
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
imagePicker.showsCameraControls = YES;
}
[self presentModalViewController:imagePicker animated:YES];
[imagePicker release];
}
When if the user cancels out from the image picker, we go back to the main view. No problem. If however they complete image selection (either through taking a photo or choosing an existing one) then we call:
'- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
// Ok, dismiss the modal picker, bring up the activity indicator and dispatch a thread
// that will do all the photo processing ...
BOOL isFromCamera = picker.sourceType == UIImagePickerControllerSourceTypeCamera;
// Dismiss the picker view controller
[picker dismissModalViewControllerAnimated:NO];
// And remove our view from the list of views
[self.view removeFromSuperview];
if (isFromCamera)
{
// Declare the completion block to use
ALAssetsLibraryWriteImageCompletionBlock compBlock = ^(NSURL *assetURL, NSError *error) {
if (error != nil || assetURL == nil) {
NSLog(#"Failed to save photo: %#", error);
[delegate photoSetURLForImage:nil];
}
else {
NSLog(#"URL is : %#", [assetURL absoluteString]);
[delegate photoSetURLForImage:assetURL];
}
};
ALAssetsLibrary* library = [[[ALAssetsLibrary alloc] init] autorelease];
[library writeImageToSavedPhotosAlbum:cgimage
metadata:meta
completionBlock:compBlock];
return;
}
else {
// Use the URL to get the metadata for the image that was picked ...
NSURL* url = [(NSDictionary*)info objectForKey:#"UIImagePickerControllerReferenceURL"];
if (url) {
// Define a result and failure block for fetching from the ALAsset
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset* myasset)
{
ALAssetRepresentation *rep = [myasset defaultRepresentation];
NSLog(#"URL is : %#", [[rep url] absoluteString]);
[delegate photoSetURLForImage:[rep url]];
};
// And also define a failure block
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
[delegate photoSetURLForImage:nil];
};
ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
[assetslibrary assetForURL:url
resultBlock:resultblock
failureBlock:failureblock];
return;
}
}
// If we get here, something went very wrong
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"Error" message:#"An error occured while retrieving the image" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil] autorelease];
[alert show];
}
Now, the first time I run through this code, all works great. As soon as I hit "use" on the image I took with the camera, or I select an image from the camera roll, the UIImagePicker view and the view that launches it go away and I'm back at the main view while I wait for the ALAsset calls to do their thing.
The problem starts when I try to repeat this process a second time. After selecting an image or taking one, the processing begins, but the views do not go away, until all of the processing by the ALAsset calls has completed. I can't figure out why this is. Why it works the first time, but not anytime after. Is there some caching mechanism I have to clear? Anyways, if anybody can offer up some advice, I'd be very grateful.
Thanks,
J
Yet again it appears I'll be answering my own question. This is starting to become a habit lol.
So it appears that the ALAsset calls aren't spinning off a new thread which is what I thought they were supposed to do. My bad. So to resolve, it was just a matter of spawning a new thread to do all the ALAsset stuff, and in the main thread dismissing my picker and what not.
Problem solved, yay!

Exception in iPhone app : Modal transition is already in progress

I have what I believe is a fairly simple application at the moment based on a few tutorials cobbled together. I'm using XCode 3.2.3 in OSX 10.6.4. It started as a standard iPhone "Window Based Application". Using interface builder I have added a Tab Bar Controller using the O'Reilly video tutorial here:
http://broadcast.oreilly.com/2009/06/tab-bars-and-navigation-bars-t.html
In the first Tab I have a standard UIView with two buttons. Both call the same function to display a UIImagePickerController:
-(IBAction) btnPhotoClicked:(id)sender {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
if((UIButton *)sender == btnChoosePhoto)
{
imagePicker.allowsEditing = YES;
imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
} else {
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
}
[self presentModalViewController:imagePicker animated:YES];
[imagePicker release];
}
I am running the code inside an emulator so only ever click the button called Choose Photo. When the dialogue is released with a photo chosen this function runs:
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSURL *mediaUrl;
mediaUrl = (NSURL *)[info valueForKey:UIImagePickerControllerMediaURL];
if (mediaUrl == nil)
{
imagePuzzle = (UIImage *) [info valueForKey:UIImagePickerControllerEditedImage];
if(imagePuzzle == nil)
{
//--- Original Image was selected ---
imagePuzzle = (UIImage *) [info valueForKey:UIImagePickerControllerOriginalImage];
}
else {
//--- Get the edited image ---
//--- If it was successful the above valueForKey:UIImagePickerControllerEditedImage
//--- would have assigned it already.
}
}
else {
//--- Muppet selected a video
}
// Animate the picker window going away
[picker dismissModalViewControllerAnimated:YES];
ImageViewController *imageViewController = [[ImageViewController alloc] init];
imageViewController.delegate = self;
[self presentModalViewController:imageViewController animated:YES];
[imageViewController release];
}
This is where my problem lies. I've tried many different hacks and iterations but the above code is the simplest to present the problem. When the imageViewController is displayed as a modal dialogue the following exception is thrown:
2010-07-09 15:29:29.667 Golovomka[15183:207] *** Terminating app due to uncaught
exception 'NSInternalInconsistencyException', reason: 'Attempting to begin a modal
transition from <NewViewController: 0x5915f80> to <ImageViewController: 0x594a350>
while a transition is already in progress. Wait for viewDidAppear/viewDidDisappear
to know the current transition has completed'
How do I cure this? I have tried delays and other tricks but do not really understand how I'm supposed to use viewDidAppear or viewDidDisappear to help me. Also of note is that a very basic application with one view loading the picker then displaying another view with the image in does not produce the error. Any help gratefully received.
To address the specific issue described here, you could add the viewDidAppear method in your class:
-(void)viewDidAppear:(BOOL)animated
{
if (/*just visited ImagePicker*/)
{
ImageViewController *imageViewController = [[ImageViewController alloc] init];
imageViewController.delegate = self;
[self presentModalViewController:imageViewController animated:YES];
[imageViewController release];
}
}
Remove those lines from below your call:
[picker dismissModalViewControllerAnimated:YES];
So, whenever your class self appears (is displayed), it will call viewDidAppear... Since this most likely isn't really what you want all the time, you could add some variables to set/clear that defines whether or not to immediately present the imageViewController when self is displayed. Something like "If coming from image picker, show the imageViewController, otherwise do nothing".
That said, imho, pushing modal views is should generally be done in response to a user action and I would maybe rethink the user experience here - e.g. add a subview instead of pushing a modal view which you could do where your currently have the code - but if you're just playing around with some tutorials that should solve the NSInternalInconsistencyException. :) Cheers!
In iOS 5.0 and above you can use
[self dismissViewControllerAnimated:YES completion:^{
//present another modal view controller here
}];
I ran into this issue quite a few times. I recently started using this simple fix:
When I am going to present a new modal view controller immediately after dismissing another modal view controller, I simply dismiss the first one with argument NO in dismissModalViewControllerAnimated:.
Since the second view is presented with an animation, you hardly notice that the first one goes away fast. And you never get the transitions conflict.
I was having the same problem when i wanted to present an MFMailComposeViewController immediately after dismissing the UIImagePickerController. Heres what i did:
I removed the [imagePicker release]; statement from where i was presenting the image picker and put it in didFinishPickingMedia callback.
I used [self performSelector:#selector(presentMailComposer:) withObject:image afterDelay:1.0f];
Here's my code:
Displaying Image Picker
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
NSArray *media = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
if ([media containsObject:(NSString*)kUTTypeImage] == YES) {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
[picker setMediaTypes:[NSArray arrayWithObject:(NSString *)kUTTypeImage]];
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.delegate = self;
[self presentModalViewController:picker animated:YES];
//[picker release];
}
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Unavailable!"
message:#"Could not open the Photo Library."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
Image Picker Delegate Callback - didFinishPickingMedia
NSString *mediaType = [info valueForKey:UIImagePickerControllerMediaType];
if([mediaType isEqualToString:(NSString*)kUTTypeImage]) {
UIImage *photoTaken = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
//Save Photo to library only if it wasnt already saved i.e. its just been taken
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
UIImageWriteToSavedPhotosAlbum(photoTaken, self, #selector(image:didFinishSavingWithError:contextInfo:), nil);
}
//Pull up MFMailComposeView Controller
[self performSelector:#selector(composeMailWithPhoto:) withObject:photoTaken afterDelay:1.0f];
}
[picker dismissModalViewControllerAnimated:YES];
[picker release];
Display Mail Composer View
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *mailPicker = [[MFMailComposeViewController alloc] init];
mailPicker.mailComposeDelegate = self;
// Fill out the email fields and Attach photograph to mail
static NSString *imageType = #"image/jpeg";
NSString *imageName = [NSString stringWithString:#"MyCoffeeCup.jpg"];
NSData *imageData = UIImageJPEGRepresentation(image, 1.0);
[mailPicker addAttachmentData:imageData mimeType:imageType fileName:imageName];
[mailPicker setToRecipients:[NSArray arrayWithObject:#"hello#xische.com"]];
[self presentModalViewController:mailPicker animated:YES];
//[self.navigationController pushViewController:mailPicker animated:YES];
[mailPicker release];
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Unavailable!"
message:#"This device cannot send emails."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}