When I launch my universal app on the iPad in landscape mode, the app won't recognize taps or gestures on the side of the screen closest to the home button. I've gone over every similar question on stackoverflow trying to get to the bottom of this but I just can't get it to work.
It would appear that one of the views in the view hierarchy is in portrait rather than landscape but I can't find any views that are incorrectly sized. Also, note that the app works fine when launched in portrait mode and rotates correctly and recognizes all taps after rotation. But when launched in landscape there is always a "dead zone" on one side of the screen where taps are not recognized.
The view hierarchy looks like:
UIWindow (frame: 0, 0, 1024, 768)
UINavigationController
UIViewController
UIView (frame: 0, 0, 1024, 768)
GLView (frame: 0, 0, 1024, 768)
I have checked the frames and bounds for the views, sets clipsToBounds = YES and changed the background colors of them to see if they are not being layed out correctly but they are all occupying the entire screen (1024x768) as intended.
I have subclassed UIWindow and overridden the sendEvent: method and it is NOT being called at all when I tap on the dead zone at the left hand side of the screen. Does this indicate where the problem might lie?
Here is my app delegate where the window is created:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Create the main window
CGRect windowFrame = [[UIScreen mainScreen] bounds];
if (application.statusBarOrientation == UIInterfaceOrientationLandscapeLeft || application.statusBarOrientation == UIInterfaceOrientationLandscapeRight) {
windowFrame = CGRectMake(0, 0, windowFrame.size.height, windowFrame.size.width);
}
window_ = [[MyUIWindow alloc] initWithFrame:windowFrame];
navController_ = [[BFNavigationController alloc] init];
navController_.navigationBarHidden = YES;
// for rotation and other messages
[[CCDirector sharedDirector] setDelegate:navController_];
// set the Navigation Controller as the root view controller
[window_ setRootViewController:navController_];
bfViewController_ = [[BFViewController alloc] init];
[navController_ pushViewController:bfViewController_ animated:NO];
// make main window visible
[window_ makeKeyAndVisible];
return YES;
}
And this is the UIViewController's viewDidLoad method where the UIView is configured:
- (void)viewDidLoad
{
[super viewDidLoad];
UIWindow *mainWindow = (UIWindow*)[UIApplication sharedApplication].windows.firstObject;
self.view.frame = mainWindow.bounds;
self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
self.view.autoresizesSubviews = YES;
self.view.clipsToBounds = YES;
[self initCocos2D];
}
And here's where the GLView is created:
- (void)initCocos2D {
UIWindow *window = [[[UIApplication sharedApplication] windows] firstObject];
CCDirector *director = (CCDirectorIOS*) [CCDirector sharedDirector];
CCGLView *glView = [CCGLView viewWithFrame:[window bounds]
pixelFormat:kEAGLColorFormatRGB565
depthFormat:0
preserveBackbuffer:NO
sharegroup:nil
multiSampling:NO
numberOfSamples:0];
glView.clipsToBounds = YES;
[glView setMultipleTouchEnabled:YES];
director.wantsFullScreenLayout = YES;
// set FPS at 60
[director setAnimationInterval:1.0/60];
// attach the openglView to the director
[director setView:glView];
// 2D projection
[director setProjection:kCCDirectorProjection2D];
// Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices
if( ! [director enableRetinaDisplay:YES] )
CCLOG(#"Retina Display Not supported");
// Default texture format for PNG/BMP/TIFF/JPEG/GIF images
// It can be RGBA8888, RGBA4444, RGB5_A1, RGB565
// You can change this setting at any time.
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
// Assume that PVR images have premultiplied alpha
[CCTexture2D PVRImagesHavePremultipliedAlpha:YES];
[self.view insertSubview:glView atIndex:0];
[[CCDirector sharedDirector] startAnimation];
}
The gesture recognizers are added to the glView and handled by the cocos2D game layer:
UIPanGestureRecognizer *panGestureRecognizer = [[[UIPanGestureRecognizer alloc] initWithTarget:layer action:#selector(handlePan:)] autorelease];
[[CCDirector sharedDirector].view addGestureRecognizer:panGestureRecognizer];
UITapGestureRecognizer *tapGestureRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:layer action:#selector(handleTap:)] autorelease];
[[CCDirector sharedDirector].view addGestureRecognizer:tapGestureRecognizer];
UIPinchGestureRecognizer *pinchGestureRecognizer = [[[UIPinchGestureRecognizer alloc] initWithTarget:layer action:#selector(handlePinch:)] autorelease];
[[CCDirector sharedDirector].view addGestureRecognizer:pinchGestureRecognizer];
This is driving me crazy; any help would be much appreciated.
I was able to solve this with the following steps:
Create the UIWindow with [UIScreen mainscreen].bounds as its frame, which will always be portrait. I was previously changing the window bounds depending on orientation which is incorrect.
Use the current status bar orientation to determine portrait/landscape and set the lowest level UIView's frame accordingly. (ie 1024x768 for landscape and 768x1024 for portrait)
Set the glView's frame to its parent UIView's bounds, NOT the window's bounds
Related
I added a UIScrollView to my main UIViewController but for some reason i can't see anything. I painted the scroll view in grey in order to see the actual size, but all i get is a white screen.
Here is my very simple code:
-(void) viewDidLoad{
[super viewDidLoad];
UIScrollView *scroll;
[scroll setBackgroundColor:[UIColor grayColor]];
CGRect screenRect = [[UIScreen mainScreen] bounds];
_orientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsPortrait(_orientation)){
scroll.showsHorizontalScrollIndicator = YES;
}else{
scroll.showsVerticalScrollIndicator = YES;
}
scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(screenRect.origin.x,
screenRect.origin.y,
screenRect.size.width,
screenRect.size.height)];
[self.view addSubview:scroll];
}
The view size might change, i'm working on a changing iOS device size.
I have an iOS app that uses UIKit for the most part, bot for two UIViewControllers, I use Cocos2D.
I set up Cocos2D like so:
#implementation CocosScreen1 {
#private
CCDirectorIOS* director;
CCScene* scene;
CCLayer* comicLayer;
UINavigationBar* navBar;
UIView* bottomOptionsBar;
CCGLView *glView;
}
-(void) setupCocos2d; {
glView = [CCGLView viewWithFrame:self.view.bounds pixelFormat:kEAGLColorFormatRGB565 depthFormat:0 preserveBackbuffer:NO sharegroup:nil multiSampling:NO numberOfSamples:0];
director = (CCDirectorIOS*) [CCDirector sharedDirector];
director.wantsFullScreenLayout = YES;
[director setAnimationInterval:1.0/60]; // set FPS at 60
[director setView:glView]; // attach the openglView to the director
[director setDelegate:self]; // for rotation and other messages
[director setProjection:kCCDirectorProjection2D]; // 2D projection
if( ! [director enableRetinaDisplay:YES] )
CCLOG(#"Retina Display Not supported");
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
CCFileUtils *sharedFileUtils = [CCFileUtils sharedFileUtils];
[sharedFileUtils setEnableFallbackSuffixes:NO];
[sharedFileUtils setiPhoneRetinaDisplaySuffix:#"-hd"];
[sharedFileUtils setiPadSuffix:#"-ipad"];
[sharedFileUtils setiPadRetinaDisplaySuffix:#"-ipadhd"];
[CCTexture2D PVRImagesHavePremultipliedAlpha:YES];
CCScene* bottomScene = [CCScene node];
[director pushScene:bottomScene];
scene = [CCScene node];
comicLayer = [[AgComicLayer alloc] initWithOptionsView:optionsParentView withReaderScreenReference:self];
UIBarButtonItem* backButton = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonSystemItemCancel target:self action:#selector(backButtonTapped)];
[self.navigationItem setBackBarButtonItem:backButton];
}
And then the two screens have this called before they appear:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.view insertSubview:director.view atIndex:0];
[[CCDirector sharedDirector] startAnimation];
[director pushScene:scene];
}
And then the two screens have this called when they disappear:
-(void) viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[[CCDirector sharedDirector] pause];
[director popScene];
}
What happens is that one of the screens works perfectly fine. However, the other Cocos2D scene suffers an insane amount of framerate drop from 60 to 4. And it fails to start animations performed via the runAction method.
I need to solve the framerate issue and have the second scene resume correctly and play animation. What is wrong with my implementation? Are there best practices for what I am trying to achieve?
Resume the director. You're pausing it, but never resume. Pause means reducing the fps to 4. Since the director is a singleton pause/resume affects both views.
I have an app that I want to only work with in Landscape.
For the first time ever, I'm foregoing IB and trying to set up all my views programmatically, so I'm creating a view and adding a bunch of subviews in loadView method:
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
// Create a GMSCameraPosition that tells the map to display the
// coordinate -33.86,151.20 at zoom level 6.
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-33.86
longitude:151.20
zoom:6];
self.mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera];
self.mapView.myLocationEnabled = YES;
self.mapView.delegate = self;
self.mapView.mapType = kGMSTypeHybrid;
self.mapView.frame = self.view.frame;
[self.view addSubview:self.mapView];
// add the toolbar
UIToolbar* toolbar = [[UIToolbar alloc] init];
toolbar.frame = CGRectMake(0, self.view.frame.size.height - 44, self.view.frame.size.width, 44);
toolbar.barStyle = UIBarStyleDefault;
NSMutableArray* items = [NSMutableArray array];
[items addObject:[[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"location-arrow.png"]
style:UIBarButtonItemStyleBordered
target:self
action:#selector(locateMe:)]];
[items addObject:[[UIBarButtonItem alloc] initWithTitle:#"Tools"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(toolsButtonTapped:)]];
[toolbar setItems:items];
[self.view addSubview:toolbar];
In my project settings, I have disabled both portrait orientations. I also have this in my root view controller:
// Enforce Landscape Orientation
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeRight;
}
My problem is that the simulator starts in landscape mode, but all of the views are sized for portrait - so the bottom chunk of my views are below the screen and the right side of my screen is a big empty region.
I tried fixing this by switching the width and height of the application frame in the first line, but then that leaves some empty vertical room on the left edge of the screen for the status bar.
So, what's the correct way of doing what I'm trying to do?
Instead of using [[UIScreen mainScreen] applicationFrame]
try using [[[[[self view] window] rootViewController] view] bounds]
The bounds will represent the width and height correctly in Landscape orientation, because the bounds will take into account the transform (rotation) that has been applied, while the frame will not.
To see what I mean, set a breakpoint, and in the debugger print out the description of the top level view lldb> po [[[[self view] window] rootViewController] view]
You'll see that the view has a rotation transform and that its frame does not represent the dimensions of the screen in landscape, but represents the dimensions in portrait!
The long way to calculate the correct applicationFrame would be
BOOL iOS7 = NO;
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:#"7.0" options:NSNumericSearch] != NSOrderedAscending)
iOS7 = YES;
CGRect theFrame;
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
CGRect screenBounds = [[UIScreen mainScreen] bounds];
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
theFrame.origin = CGPointZero;
theFrame.size.width = screenBounds.size.height;
theFrame.size.height = screenBounds.size.width;
if (iOS7 == NO) {
// statusBarFrame will be CGRectZero if not visible, so this is safe
theFrame.size.height -= statusBarFrame.size.width; // because we're in landscape orientation
}
}
else {
theFrame = screenBounds;
if (iOS7 == NO) {
// statusBarFrame will be CGRectZero if not visible, so this is safe
theFrame.size.height -= statusBarFrame.size.height; // because we're in portrait orientation
}
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (UIInterfaceOrientationIsLandscape( interfaceOrientation))
{
return YES;
}
return NO;
}
Put this code Appdelegate .M.....
Put this in current viewcontroller
// ios 5
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (UIInterfaceOrientationIsLandscape( interfaceOrientation)) {
return YES;
}
return NO;
}
// ios6
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
Normally, the coco2d OpenGL view is launched from the appdelegate.
Method1: luanching from appdelegate
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
// Init the window
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Try to use CADisplayLink director
// if it fails (SDK < 3.1) use the default director
if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
[CCDirector setDirectorType:kCCDirectorTypeDefault];
CCDirector *director = [CCDirector sharedDirector];
// Init the View Controller
viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
viewController.wantsFullScreenLayout = YES;
//
// Create the EAGLView manually
// 1. Create a RGB565 format. Alternative: RGBA8
// 2. depth format of 0 bit. Use 16 or 24 bit for 3d effects, like CCPageTurnTransition
//
//
EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
pixelFormat:kEAGLColorFormatRGB565 // kEAGLColorFormatRGBA8
depthFormat:0 // GL_DEPTH_COMPONENT16_OES
];
// attach the openglView to the director
[director setOpenGLView:glView];
but I need to able to launch the OpenGL view from a viewcontroller
so, inside my viewcontroller's viewdidload function I did this
method2: launch from viewcontroller
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.frame = CGRectMake(0, 0, 320, 480);
// Create an CCGLView with a RGB565 color buffer, and a depth buffer of 0-bits
CCGLView *glView = [CCGLView viewWithFrame:CGRectMake(0,0,320,480)
pixelFormat:kEAGLColorFormatRGB565 //kEAGLColorFormatRGBA8
depthFormat:0 //GL_DEPTH_COMPONENT24_OES
preserveBackbuffer:NO
sharegroup:nil
multiSampling:NO
numberOfSamples:0];
director_ = (CCDirectorIOS*) [CCDirector sharedDirector];
director_.wantsFullScreenLayout = YES;
// Display FSP and SPF
[director_ setDisplayStats:YES];
// set FPS at 60
[director_ setAnimationInterval:1.0/60];
// attach the openglView to the director
[director_ setView:glView];
// for rotation and other messages
[director_ setDelegate:self];
// 2D projection
[director_ setProjection:kCCDirectorProjection2D];
// Run the intro Scene
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
// and add the scene to the stack. The director will run it when it automatically when the view is displayed.
[director_ pushScene: [helloworld scene]];
method2 is not working I don't see any scene, nothing shows up. so the question is how to make OpenGL View present from a viewcontroller?
Have you tried as in the class reference attachInView: method?
It'be of the form
[director_ attachInView:self.view];
else you can do it by
[self.view addSubview:director_.view];
Hope this helps you.
since CCDirectorIOS inherits CCDirector class which in turn inherits uiviewcontroller class, and i am assuming you are using uinavigationcontroller, therefore just push the director_ class at the end viewdidload
[self.navigationController pushViewController:director_ animated:YES];
Unfortunately, I don't understand UIViewControllers very well, nor do I understand exactly how they mesh with cocos2d scenes. However, I am able to load a standard Game Center leaderboard view on top of my cocos2d (landscape-only) game successfully on the iPhone. But, my game is a universal app and when I try it on iPad, the Game Center view loads in portrait orientation, is about half the size it should be (fills up only one quarter of the screen), and is not centered. When I rotate the device, the Game Center view orients itself to landscape but gets really stretched out and looks like it wasn't designed for iPad in landscape view.
Does anyone have any advice?
- (void) showLeaderboard
{
if(![MyAppDelegate isGameCenterAPIAvailable])
return;
if ([GKLocalPlayer localPlayer].isAuthenticated == YES)
{
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
tempVC = [[RootViewController alloc] init];
GKLeaderboardViewController *leaderboard = [[[GKLeaderboardViewController alloc] init] autorelease];
if (leaderboard != NULL)
{
leaderboard.leaderboardDelegate = self;
[[[CCDirector sharedDirector] openGLView] addSubview:tempVC.view];
// Pause game
[[CCDirector sharedDirector] pause];
[tempVC presentModalViewController:leaderboard animated: NO];
leaderboard.view.transform = CGAffineTransformMakeRotation(CC_DEGREES_TO_RADIANS(0.0f));
[leaderboard.view setCenter:CGPointMake(screenSize.height/2, screenSize.width/2)];
leaderboard.modalPresentationStyle = UIModalPresentationCurrentContext;
}
}
}
- (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
[tempVC dismissModalViewControllerAnimated: YES];
[tempVC.view.superview removeFromSuperview];
[tempVC release];
tempVC = nil;
// Resume game
[[CCDirector sharedDirector] resume];
}
Please take a look at Implementing iAds in Cocos2d Application.
[self.view addSubview:self.bannerView];
In this tutorial, addSubview UIView object to RootViewController.view, that is in the cocos2d application template, instead of EAGLView.