So far I am using the cocos2d engine to create a game and I want to implement a QR reader.
So far I've created a CCScene, the CCScene has a menu button and it leads to a onScan Method:
-(void) onScan:(id)sender{
// ADD: present a barcode reader that scans from the camera feed
ZBarReaderViewController *reader = [ZBarReaderViewController new];
reader.readerDelegate = self;
ZBarImageScanner *scanner = reader.scanner;
// TODO: (optional) additional reader configuration here
// EXAMPLE: disable rarely used I2/5 to improve performance
[scanner setSymbology: ZBAR_I25
config: ZBAR_CFG_ENABLE
to: 0];
// present and release the controller
[self presentModalViewController: reader
animated: YES];
[reader release];
}
I understand that this works on a standard ViewController class but I am wondering how to best implement this to work with the cocos2d engine on my CCScene
Any help would be appreciated Thanks!
If you come from a typical cocos2d-iphone project which was created using the Xcode template, I think you should have a RootViewController class in your app. The only instance of the RootViewController is created in applicationDidFinishLaunching: in AppDelegate.m.
viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
You can somehow save a global reference of your root UIViewController, for example, to implement a class method in RootViewController:
static RootViewController *sharedInstance_ = nil;
#implementaion RootViewController
+ (RootViewController *)sharedInstance {
if (!sharedInstance_) {
sharedInstance_ = [[RootViewController alloc] initWithNibName:nil bundle:nil];
}
return sharedInstance_;
}
and change the initialization in AppDelegate.m to:
viewController = [[RootViewController sharedInstance] retain];
then use it to present your UIViewController:
[[RootViewController sharedInstance] presentModalViewController:reader
animated:YES];
In cocos2d-iphone, the CCDirector IS the UIViewController.
So, just do this:
[[CCDirector sharedDirector] presentModalViewController:reader animated:YES]
UIView* glView = [CCDirector sharedDirector].view;
UIView* window = glView.superview;
[window addSubview:reader.view];
Related
I trying to write rhodes native extension extension for opening iOS MailComposer.
Now code "works", but MFMailComposeViewController not displaying and xcode shows warning:
Attempt to present <MFMailComposeViewController: 0x12b60840> on <Iosmailto: 0xc1898d0> whose view is not in the window hierarchy!
I read that UIViewControllers must be in hierarchy, but i can't provide this from Rhodes ruby code or clear-C extension wrapper.
Now i'm trying to present my UIViewController with MFMailComposeController from [UIApplication sharedApplication].keyWindow.rootViewController but mail composer not show yet.
interface from iosmailto.h:
#interface Iosmailto : UIViewController<MFMailComposeViewControllerDelegate>
{
}
#property(nonatomic, assign) id delegate;
//-(IBAction)send_mail:(id)sender;
#end
implementation from iosmailto.m:
#implementation Iosmailto
- (void)viewDidLoad {
[super viewDidLoad];
[self send_mail];
}
- (void)send_mail {
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *composer = [[MFMailComposeViewController alloc] init];
composer.mailComposeDelegate = self;
[composer setSubject:[NSString stringWithUTF8String:subj]];
NSArray *recipients_array = [NSArray arrayWithObject:[NSString stringWithUTF8String:recipients]];
[composer setToRecipients:recipients_array];
NSString *composerBody = [NSString stringWithUTF8String:message];
[composer setMessageBody:composerBody isHTML:NO];
[composer setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentModalViewController:composer animated:YES];
[composer release];
} else {
NSLog(#"Device is unable to send email in its current state.");
}
}
/* some unnecessary for this question methods */
#end
And C function defined in iosmailto.m thats called from Rhodes:
// find root vc
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
// find topmost vc
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
iosmailer = [[Iosmailto alloc] init];
iosmailer.delegate = topController;
[topController presentViewController:iosmailer animated:YES completion:nil];
This app is not for AppStore. Hacks is welcome if needed.
Update init for Iosmailto:
- (id)init
{
self = [super initWithNibName:nil bundle:nil];
return self;
}
Update 2 The short version of this code (without UIViewController wrapper) was my starting point. That's don't work too. And this have the same warning and one more:
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *composer = [[MFMailComposeViewController alloc] init];
composer.mailComposeDelegate = (id<MFMailComposeViewControllerDelegate>)topController ;
[composer setSubject:[NSString stringWithUTF8String:subj]];
NSArray *recipients_array = [NSArray arrayWithObject:[NSString stringWithUTF8String:recipients]];
[composer setToRecipients:recipients_array];
NSString *composerBody = [NSString stringWithUTF8String:message];
[composer setMessageBody:composerBody isHTML:NO];
[composer setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[topController presentModalViewController:composer animated:YES];
[composer release];
} else {
}
Use this code to present view controller
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:composer
animated:YES
completion:nil];
Ok I think the problem is that your UIViewController doesn't have anything in its view property because you aren't using a .xib file to initialize it. See this question which programmatically creates a UIView and assigns it to the UIViewController's view property. I think that's the right way to go. See the implementation of their -(void)loadView method.
Your use-case is also a little strange, since it looks like your Iosmailto UIViewController doesn't have any content, and you're just using it as a wrapper around MFMailComposeViewController--you might consider reimplementing your C method to directly create a MFMailComposeViewController without the unnecessary layer of indirection of this unnecessary UIViewController that doesn't display anything itself.
OK, so I'm trying to show Apple Game Center Leaderboards called from within my cocos2d game.
I've had some trouble doing so.
I did eventually stumble upon this and I implemented the following in one of my CCScene classes (I slightly modified the original code to prevent a compiler warning).
- (void)showLeaderboardForCategory:(NSString *)category
{
// Create leaderboard view with default Game Center style
leaderboardController = [[GKLeaderboardViewController alloc] init];
// If view controller was successfully created...
if (leaderboardController != nil)
{
// Leaderboard config
leaderboardController.leaderboardDelegate = self; // leaderboardController will send messages to this object
leaderboardController.category = category;
leaderboardController.timeScope = GKLeaderboardTimeScopeAllTime;
// Create an additional UIViewController to attach the GKLeaderboardViewController to
vc = [[UIViewController alloc] init];
// Add the temporary UIViewController to the main view
[[CCDirector sharedDirector].view.window addSubview:vc.view];
// Tell UIViewController to present the leaderboard
[vc presentModalViewController:leaderboardController animated:YES];
}
}
- (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
[vc dismissViewControllerAnimated:YES completion:nil];
}
And, it works! At least when I call it, it does display the Leaderboard properly.
The only problem is, when I tap "Done" on the Leaderboard and the modal view dismisses, my CCScene no longer responds to tap events.
What do I need to do to regain responsiveness?
Refer sample plain cocos2d project:
-(void)showLeaderboard
{
GKLeaderboardViewController *leaderboardViewController = [[GKLeaderboardViewController alloc] init];
leaderboardViewController.leaderboardDelegate = self;
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] presentModalViewController:leaderboardViewController animated:YES];
[leaderboardViewController release];
}
Delegate Function:
-(void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
I am trying to push an opengl UIView to my navigation controller like this
GraphViewController *gvc = [[GraphViewController alloc] initWithTicker:[listOfItems objectAtIndex:indexPath.row]];
[self.navigationController pushViewController:gvc animated:YES];
[gvc release];
The initWithTicker method looks like this
-(id) initWithTicker:(NSString*)ticker{
self = [super initWithNibName:nil bundle:nil];
if (self) {
self.title = ticker;
EAGLView *eagl = [[EAGLView alloc] initWithFrame:[UIScreen mainScreen].bounds];
eagl.animationInterval = 1.0 / 60.0;
[eagl startAnimation];
self.view = eagl;
}
return self;
}
When I go back and forward in my UINavigationController, the drawView method (in EAGLView) keeps looping. Furthermore, if I pushViewController again, the first one does not stop and a new one is created! I've tried making this an instance variable so only one is created and it has the same effect. I would be grateful if anyone has insight as to why this is happening
sergio Suggestion:
-(id) initWithTicker:(NSString*)ticker{
self = [super initWithNibName:nil bundle:nil];
if (self) {
self.title = ticker;
}
return self;
}
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
eagl = [[EAGLView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.view = eagl;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
eagl.animationInterval = 1.0 / 60.0;
[eagl startAnimation];
[super viewDidLoad];
}
same behaviour.
---This is how I fixed my drawView looping problem--
-(void)viewDidAppear:(BOOL)animated {
[eagl startAnimation];
[super viewDidAppear:animated];
}
-(void)viewDidDisappear:(BOOL)animated {
[eagl stopAnimation];
[super viewDidDisappear:animated];
}
--Craigs solution --
if(graphView == nil){
graphView = [[GraphViewController alloc] initWithTicker:[listOfItems objectAtIndex:indexPath.row]];
}else{
[graphView release];
graphView = [[GraphViewController alloc] initWithTicker:[listOfItems objectAtIndex:indexPath.row]];
}
Are you creating a new GraphViewController every time you want to push one onto your navigation stack? If so, it doesn't really matter how you're handling the creation of your EAGLView instance variable, since you're never going to be interacting with that view controller again anyway.
For example:
User taps something, a new GraphViewController is pushed on the stack
User goes back, this view controller continues to run
Return to 1. and repeat (thus creating a SECOND GraphViewController, and then a third, and then a fourth... etc.)
What you should probably be doing is maintaining your GraphViewController as an instance variable, and only creating it once. This will ensure that you're in turn only creating one EAGLView.
if (_graphViewController == nil) {
_graphViewController = [[GraphViewController alloc] initWithTicker:[listOfItems objectAtIndex:indexPath.row]];
}
[self.navigationController pushViewController:_graphViewController animated:YES];
Then, be sure to release the view controller in your dealloc method if you're going to be maintaining it as an ivar.
Would you try executing this code of yours:
EAGLView *eagl = [[EAGLView alloc] initWithFrame:[UIScreen mainScreen].bounds];
eagl.animationInterval = 1.0 / 60.0;
[eagl startAnimation];
self.view = eagl;
inside of loadView? I am not sure about why your view is behaving like you say, but that is the place where you are supposed to build your UI... so it might make a difference...
Furthermore, I would call [eagl startAnimation]; only in viewDidLoad...
Hope it helps...
My iPhone app badly leaks when flipping back and forth between a main uiviewcontroller and a help uiviewcontroller .
Here is the source of the main view, followed by source of the help view.
MAIN VIEW - FLIP TO HELP.....................
// Changes from operational view to Help view.
- (IBAction)showHelp:(id)sender
{
// End trial mode:
self.stop_trial_if_started;
self.rename_trial_if_edited;
// Switch to trial help:
help_view_context = 0;
HelpView *controller = [[HelpView alloc] initWithNibName:#"HelpView" bundle:nil];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
[controller release];
}
HELP VIEW - INIT.............................
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
help_scroll.editable = FALSE;
return;
}
HELP - RETURN TO MAIN VIEW.........................
// User clicked the button to return to operational view:
- (IBAction)done:(id)sender {
NSLog(#"help- done");
if( help_view_context == 0 ) {
[self.delegate trial_help_DidFinish:self];
}else{
[self.delegate file_help_DidFinish:self];
}
}
MAIN VIEW - RETURN FROM HELP...............................
// Inits operational view when user changes from Help view back to operational view.
- (void)trial_help_DidFinish:(HelpView *)controller {
NSLog(#"trial_help_DidFinish");
[self dismissModalViewControllerAnimated:YES];
self.init_trial_operation;
}
You are creating a controller with ref count of 1 and a local reference each time showHelp: is called:
HelpView *controller = [[HelpView alloc] initWithNibName:#"HelpView" bundle:nil];
you are losing your reference to it at the end of this method.
You happen to have references to it in done: (self) and *_help_didFinish (controller), but you never release it in either of those locations. Dismissing the controller is fine, but you also have to release it.
(Another option would be to never create a second one, and maintain an iVar to the original.)
You could well be leaking on this line
controller.delegate = self;
What is your property declaration for the delegate. If it's anything other than assign, then you either need to change it (preferred option) or make sure you are releasing it in the dealloc method of HelpView controller.
I'm switching between two iPhone view controllers: A initial viewController running an OpenGL view of a 3D animation, and a second viewController with a UIKit view of program settings. The OpenGL animation runs fine when I start the app, and I can switch to the second VC to alter program settings. When I switch back from the settings VC via a delegate, I cannot get the OpenGL animation to restart -- I'm only getting a black view.
Is there anything else I need to do to re-initialize the OpenGL-ES state when I switch back to the GL view?
My switch from the GL viewController to the settings viewController works fine:
- (IBAction) switchToSettingsView:(id) sender {
SettingsViewController *controller = [[SettingsViewController alloc] initWithNibName:#"SettingsView" bundle:nil];
controller.settingsDelegate = self;
[self stopTimer]; // Stop the animation
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
[controller release];
}
When I return from from the settings view via the following delegate, I see only a black background:
- (void) settingsViewControllerDidFinish:(SettingsViewController *)controller {
[self dismissModalViewControllerAnimated:YES];
[self startTimer];
[glView startAnimation];
[glView drawView]; // Explicitly re-drawing the GL view doesn't help
}
Adding viewDidAppear/viewWillDisappear did not help. They do get called as expected.
- (void)viewDidAppear:(BOOL)animated
{
[glView startAnimation];
}
- (void)viewWillDisappear:(BOOL)animated
{
[glView stopAnimation];
}
I am currently not using CADisplayLink in startAnimation, I'm using an NSTimer.
My OpenGL drawView is running in the GL view after I return via the delegate, but nothing appears on the display.
UPDATE: RESOLVED!!! As quixoto suggested, recreating the EAGL context helped. I also had to recreate the framebuffers. Doing either one without the other didn't fix the problem. Here's the code I used to re-initialize the graphics state:
// Re-initialize the EAGL context and framebuffers
- (void)reInitContext {
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
[context release]; // Releases the old EAGL Context
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if (!context) {
NSLog(#"RECREATING EAGL CONTEXT FAILED!");
} else {
NSLog(#"RECREATING EAGL CONTEXT SUCCEEDED!");
};
if (![EAGLContext setCurrentContext:context]) {
NSLog(#"RE-SETTING EAGL CONTEXT FAILED!");
} else {
NSLog(#"RE-SETTING EAGL CONTEXT SUCCEEDED!");
};
[self destroyFramebuffer];
[self createFramebuffer];
}