Mirror iPhone on external display with cocos2d - iphone

I want to reproduce my iPhone display for demoing purposes. I've tried
this and this, but both give me a black screen. Finally, I headed out to make my own solution. Here's what I have:
// Check for external screen.
if ([[UIScreen screens] count] > 1) {
externalWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Internal display is 0, external is 1.
externalScreen = [[[UIScreen screens] objectAtIndex:1] retain];
screenModes = [externalScreen.availableModes retain];
UIScreenMode *desiredMode = [screenModes objectAtIndex:0];
externalScreen.currentMode = desiredMode;
externalWindow.screen = externalScreen;
[screenModes release];
[externalScreen release];
CGRect rect = CGRectZero;
rect.size = desiredMode.size;
externalWindow.frame = rect;
externalWindow.clipsToBounds = YES;
externalWindow.hidden = NO;
[externalWindow makeKeyAndVisible];
[externalWindow setUserInteractionEnabled:YES];
[externalWindow setMultipleTouchEnabled:YES];
//[[CCDirector sharedDirector] attachInView:externalWindow];
Now, I can display on the external display or on my iPhone, but I can't display on both at the same time, because [[CCDirector sharedDirector] attachInView:externalWindow]; will only take one UIWindow. How can I get around this and/or get the displayed image and set it to my external display?
Thanks,
Dave

The only solution i see is to render your scene to a texture and render that texture twice as a full screen quad to the backbuffer, once for each UIWindow

Related

What piece am I missing to set an iOS background?

I have:
[A series of if statements sets backgroundImage to be something like [UIImageNamed #"Background-Default-Landscape"]; am I right to assume that the device will look for the file named Background-Default-Landscape#2x.png if it is a Retina display?]
UIImageView *backgroundView = [[UIImageView alloc] initWithImage:backgroundImage];
CGRect containerRect = CGRectZero;
containerRect.size = [backgroundImage size];
UIView *containerView = [[UIView alloc] initWithFrame:containerRect];
[containerView addSubview:backgroundView];
Right now my app is launching and once it loads displays a white screen, not a background specified (none of the backgrounds are solid white).
What am I missing to draw a background before I start putting things on the background, further below in the code?
Thanks,
--EDIT--
As far as code, I have:
- (void) renderScreen
{
int height = [[UIScreen mainScreen] bounds].size.height;
int width = [[UIScreen mainScreen] bounds].size.width;
UIImage *backgroundImage = nil;
if (height == 2048 || height == 2008 || height == 1024 || height == 1004 || height == 984)
{
backgroundImage = [UIImage imageNamed:#"Background-Default-Portrait"];
}
// Other such statements cut.
UIImageView *backgroundView = [[UIImageView alloc] initWithImage:backgroundImage];
CGRect containerRect = CGRectZero;
containerRect.size = [backgroundImage size];
UIView *containerView = [[UIView alloc] initWithFrame:containerRect];
[containerView addSubview:backgroundView];
[self.view addSubview:containerView];
Then below that are statements to draw on the background.
This method is called when the initial view loads and when it is rotated:
- (void)viewDidLoad
{
[super viewDidLoad];
[self renderScreen];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(renderScreen) name:UIDeviceOrientationDidChangeNotification object:nil];
}
The behavior is that the correct initial screen loads, then it fades to white and nothing interesting seems to happen after that.
--EDIT--
At the top, I have:
int height = [[UIScreen mainScreen] bounds].size.height;
int width = [[UIScreen mainScreen] bounds].size.width;
When I NSLog the height and width, for a retina device in landscape mode, I get a height of 1024 and a width of 768. The image displayed is a rotation of the portrait image; if I turn the device on the side the image neatly fills the whole screen but as-is the background displays a horizontally squeezed image.
Should I do anything different to be correctly obtaining height and width? I would expect for a device in landscape orientation the height would be 768 and the width would be 1024.
Every time renderScreen fires it will add two more subviews to the current controller's main view:
[containerView addSubview:backgroundView];
[self.view addSubview:containerView];
And this fires every time you rotate the device.
At least have the containerView as a property and replace it.
#property (nonatomic, strong) UIView *containerView;
and inside - (void) renderScreen
[self.containerView removeFromSuperview];
self.containerView = [[UIView alloc] initWithFrame:containerRect];
[self.view addSubview:self.containerView];
You could also add:
[self.view setNeedsDisplay];
So that the view is redrawn.

Using launch image as ViewController background image

I created my 3 launch images (Default Default#2x Default-568#2x) at their appropriate resolutions. I'd like to use these images as the background image of my initial View Controller.
However, when I designed my launch images, I did it with the statusbar in mind and left that area blank. When I try to set the background image of the View Controller to the launch image using the following code, my launch image begins at the bottom of my status bar and the blank area is visible. Essentially, my 640x1136 launch image is being squeezed into a 640x1096 space. What is the proper way to do what I'm attempting?
UIImage *backgroundImage = [[UIImage alloc]init];
if ([[UIScreen mainScreen] bounds].size.height == 568) {
backgroundImage = [UIImage imageNamed:#"Default-568h#2x"];
}
else{
backgroundImage = [UIImage imageNamed:#"Default"];
}
UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:backgroundImage];
[backgroundImageView setFrame:[[self view] bounds]];
[[self view] addSubview:backgroundImageView];
Update:
I replaced:
[backgroundImageView setFrame:[[self view] bounds]];
With:
[backgroundImageView setFrame:CGRectMake(0 ,-20,self.view.frame.size.width , self.view.frame.size.height+20)];
And it seems to be behaving the way I want now.
Try this:
[self.view setBackgroundColor:[UIColor colorWithPatternImage:backgroundImage]];
Also try not setting backgroundImageView's frame programatically. It should already be the size it should be when you call initWithImage:.
Make use of unedited image which does not contain the blank area. this will work fine.
Check replacing this [backgroundImageView setFrame:[[self view] bounds]]; with the following code.
int statusBarHeight = statusBar.frame.size.height;
backgroundImageView.frame = CGRectMake(0 ,statusBarHeight,self.view.frame.size.width , self.view.frame.size.height-statusBarHeight);

iPhone 5 vs iPhone 4 Screen sizing

I have been developing a cocos2d app for the iPhone. I have been testing it solely on my iPhone 5. Everything looks great on it, but when I test it on the 3.5 inch simulator iPhone, some buttons go missing and some labels get misplaced.
I was wondering why this happens? I had thought that everything is positioned via the cocos2d grid which then converts it into pixels, so no matter the screen size the layout should look the same.
I think the problem might be due partly to the fact that when I set the position of a menu item, (0,0) puts it at the center of the screen. I don't know why this is.
Any advice on what is going on?
iPhone 5 on the left, simulator (3.5 inch) left.
Here is the code for this screen:
CCSprite *title = [CCSprite spriteWithSpriteFrameName:#"highscoresTitle.png"];
[self addChild:title];
title.position = ccp([CCDirector sharedDirector].winSize.width/2, [CCDirector sharedDirector].winSize.height-title.contentSize.height-15);
background.position = ccp([CCDirector sharedDirector].winSize.width/2, [CCDirector sharedDirector].winSize.height/2);
play = [CCMenuItemSprite itemWithNormalSprite:[CCSprite spriteWithSpriteFrameName:#"playNew.png"] selectedSprite:[CCSprite spriteWithSpriteFrameName:#"playNew.png"] target:self selector:#selector(playScene:)];
play.position = ccp(0, -200);
CCLabelTTF *backLabel = [CCLabelTTF labelWithString:#"BACK" fontName:#"RBNo2-Light-Alternative" fontSize:25];
CCMenuItemLabel *goBack = [CCMenuItemLabel itemWithLabel:backLabel target:self selector:#selector(back:)];
goBack.position = ccp(0, -260);
CCMenuItemSprite *gameicon = [CCMenuItemSprite itemWithNormalSprite:[CCSprite spriteWithSpriteFrameName:#"gameCenter.png"] selectedSprite:[CCSprite spriteWithSpriteFrameName:#"gameCenter.png"] target:self selector:#selector(gameIcon:)];
gameicon.position = ccp(0, -150);
CCMenu *menu = [CCMenu menuWithItems:play,goBack, gameicon, nil];
[self addChild:menu z:0];
for(int i = 0; i<5 && i<length; i++){
NSString *temp = [NSString stringWithFormat:#"%d: %d", i+1,[[highscores objectAtIndex:i]intValue]];
CCLabelTTF *label = [CCLabelTTF labelWithString:temp fontName:#"RBNo2-Light-Alternative" fontSize:20];
label.position = ccp([CCDirector sharedDirector].winSize.width/2, 350-i*30);
label.visible = YES;
[self addChild:label z:100 tag:1];
}
NSString *temp = [NSString stringWithFormat:#"Last Score: %d", newHighscore];
CCLabelTTF *label = [CCLabelTTF labelWithString:temp fontName:#"RBNo2-Light-Alternative" fontSize:20];
label.position = ccp([CCDirector sharedDirector].winSize.width/2, 415);
label.visible = YES;
[self addChild:label z:100 tag:1];
Here is another example with sliders: (iPhone 5 left)
And here is the code for the sliders:
sliderCtl = [[UISlider alloc]initWithFrame:CGRectMake(100, 460, 200, 0)];
//[sliderCtl.layer setAffineTransform:CGAffineTransformMakeRotation(3.141/2)];
[sliderCtl addTarget:self action:#selector(sliderAction:) forControlEvents:UIControlEventValueChanged];
sliderCtl.backgroundColor = [UIColor clearColor];
sliderCtl.value = 1.0;
[[[[CCDirector sharedDirector] openGLView] window] addSubview:sliderCtl];
sliderEff = [[UISlider alloc]initWithFrame:CGRectMake(100, 402, 200, 0)];
[sliderEff addTarget:self action:#selector(effectsSlider:) forControlEvents:UIControlEventValueChanged];
sliderEff.backgroundColor = [UIColor clearColor];
sliderEff.value = 1.0;
[[[[CCDirector sharedDirector] openGLView] window] addSubview:sliderEff];
Thanks a lot.
Most likely, this answer will disappoint you. iPhone 5 has a number of pixels along its long edge that is different from all previous models. The coordinate system you use for placing objects in your scene is point, where 1 pt equals 2 px on devices with Retina displays.
You do make use of winSize which is provided to you by the CCDirector. This indeed helps you calculating relative positions, for example in order to center along the horizontal as I see it in some places of your code, e.g. for label. You alone are responsible for positioning your objects, with some help from anchorPoint, winSize, and related properties.
Specific example: You place goBack at a relative position of -260 pt. Note that your menu is centered on the screen by default. On an iPhone 4S and every previous model, this means an absolute position of 480 pt / 2 - 260 pt = -20 pt. No surprise that button is off screen. You will have to test for winSize.height and adjust accordingly.
I have run into the same issues and am now using the UIDevice Extension library from GitHub at the following link to identify the device. This is a little overkill unless you use other items from the identified device besides just screen size but thought you might be able to use it since I tend to always run into more needs by device type.
https://github.com/erica/uidevice-extension

pagingScrollView Slideshow

I have started trying to understand the way apple implements the photoviewer app for the iPhone after watching both videos from WWDC 2009 and 2010 about scrollViews and paging through photographs and I am taking each step very slowly to understand the processes very well before I implement it all into my app.
I have run into a couple of problems at the moment:
Firstly I have it set up to have a paging scroll view to swipe left and right between photos with a bit of space in between like on the videos but when I add an image inside a UIImageView th image is too big for the screen, I have tried adding UIViewContentModeScaleAspectFit; to no effect.
When I go through the for loop to look at each element of the array containing the images the UIViews overlap and show one photograph on top of the other, I would like to know how to separate them into the next section of the paging scroll view.
- (void)loadView{
UIImage *img0 = [UIImage imageNamed:#"29.png"];
UIImage *img1 = [UIImage imageNamed:#"33.png"];
NSMutableArray *imgArray = [[NSMutableArray alloc] initWithObjects:img0, img2, nil];
CGRect pagingScrollViewFrame = [[UIScreen mainScreen] bounds];
pagingScrollViewFrame.origin.x -= 10;
pagingScrollViewFrame.size.width += 20;
pagingScrollView = [[UIScrollView alloc] initWithFrame:pagingScrollViewFrame];
pagingScrollView.pagingEnabled = YES;
pagingScrollView.backgroundColor = [UIColor blackColor];
pagingScrollView.contentSize = CGSizeMake(pagingScrollViewFrame.size.width * [imgArray count], pagingScrollViewFrame.size.height);
self.view = pagingScrollView;
for (int i=0; i < [imgArray count]; i++) {
UIImageView *page = [[UIImageView alloc] initWithImage: [imgArray objectAtIndex:i]];
page.contentMode = UIViewContentModeScaleAspectFit;
[pagingScrollView addSubview:page];
}}
As I have mentioned I am fairly new to programming for the iPhone and am taking things slowly to fully understand, eventually this program will mimic the native app with pinching and tapping for zoom.
If you're using UIImageViews, you should make sure that your view has its clipsToBounds field set to yes. Try adding:
UIImageView *page = [[UIImageView alloc]
initWithImage:[imgArray objectAtIndex:i]];
[page setContentMode:UIViewContentModeScaleAspectFit];
[page setClipsToBounds:YES];
[pagingScrollView addSubview:page];
Then, make sure you are setting your image's frame to the correct offset within the scroll view. The frame's origin.x needs to be the width of the frame times the image index. So you need something like this:
[page setFrame:CGRectMake(i*pagingScrollViewFrame.size.width, y, width, height)];
where i is your index from your for loop.
Though in the sample code from the WWDC session you're referring to, this is done in a method called -configurePage. Have you downloaded the sample code?
-[UIImageView initWithImage:] sets the frame to be the image's size. You'll need to use page.frame = [[UIScreen mainScreen] bounds] or similar to scale images to the correct size.

How to resize a UIModalPresentationFormSheet?

I am trying to display a modal view controller as a UIPresentationFormSheet. The view appears, but I can't seem to resize it. My XIB has the proper height & width, but it seems to get overridden when I call it like this:
composeTweetController = [[ComposeTweet alloc] initWithNibName:#"ComposeTweet" bundle:nil];
composeTweetController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:composeTweetController animated:TRUE];
Any thoughts? I am using the iPhone SDK 3.2 beta 4
You are able to adjust the frame of a modal view after presenting it:
Tested in iOS 5.1 - 7.1
MyModalViewController *targetController = [[[MyModalViewController alloc] init] autorelease];
targetController.modalPresentationStyle = UIModalPresentationFormSheet;
targetController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal; //transition shouldn't matter
[self presentViewController:targetController animated:YES completion:nil];
if(floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1){
targetController.view.superview.frame = CGRectInset(targetController.view.superview.frame, 100, 50);
}else{
targetController.view.frame = CGRectInset(targetController.view.frame, 100, 50);
targetController.view.superview.backgroundColor = [UIColor clearColor];
}
Here's a method that works on iOS7 as well as iOS5 and iOS6: (arc)
-(void)presentController:(UIViewController*)controller fromRootController:(UIViewController*)rootController withSize:(CGSize)size
{
UINavigationController* nav = [[UINavigationController alloc] initWithRootViewController:controller];
nav.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
nav.modalPresentationStyle = UIModalPresentationFormSheet;
[rootController presentModalViewController:nav animated:YES];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)
{
nav.view.superview.backgroundColor = [UIColor clearColor];
nav.view.bounds = CGRectMake(0, 0, size.width, size.height);
}
else
{
nav.view.superview.bounds = CGRectMake(0, 0, size.width, size.height);
}
}
composeTweetController = [[ComposeTweet alloc] initWithNibName:#"ComposeTweet" bundle:nil];
composeTweetController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:composeTweetController animated:TRUE];
//if you want to change its size but the view will remain centerd on the screen in both portrait and landscape then:
composeTweetViewController.view.superview.bounds = CGRectMake(0, 0, 320, 480);
//or if you want to change it's position also, then:
composeTweetViewController.view.superview.frame = CGRectMake(0, 0, 320, 480);
As of iOS7 you can simply do
composeTweetController.preferredContentSize = CGSizeMake(380.0, 550.0);
Tested and works for iOS 6, using XCode 4.5
I stumbled upon my answer after reading much of the tips on here:
In the viewWillAppear:(BOOL)animated method:
[super viewWillAppear:animated];
//resize modal view
self.view.superview.bounds = CGRectMake(0, 0, 432, 680);
In the viewDidAppear:(BOOL)animated method:
CGPoint centerPoint = CGPointMake([[UIScreen mainScreen] bounds].size.width/2, [[UIScreen mainScreen] bounds].size.height/2);
self.view.superview.center = centerPoint;
I tried to add the centering code in the same place as the resizing code, but that did not work. For some reason, it only works after the view has appeared on the screen.
I surmise that it has something to do with the way that UIModalPresentationFormSheet works, because when I was stepping through in the LLDB debugger, I noticed that there was a variable _formSheetSize that was still {540, 620}. Go figure.
This will work with any UIModalTransitionStyle
#interface BaseDialog ()
#property(nonatomic,assign) CGRect origFrame;
#end
#implementation BaseDialog
-(void)viewDidLoad{
[super viewDidLoad];
self.origFrame=self.view.frame;
}
-(CGSize)formSheetSize{
return self.origFrame.size;
}
Following first code is how to present your model view controller
composeTweetController = [[ComposeTweet alloc] initWithNibName:#"ComposeTweet" bundle:nil];
composeTweetController.modalPresentationStyle = UIModalPresentationFormSheet;
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:composeTweetController];
navigationController.delegate = self;
[self presentModalViewController:navigationController animated:YES];
And In your ComposeTweet controller class
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.view.superview.frame = CGRectMake(0, 0,500,400);
}
iOS 7
So the approach of setting the superview's frame or bounds doesn't work anymore on iOS 7 (for UIModalTransitionStyleCoverVertical). You can, however, now set the background color of the superview to clear and then change the frame of the modal view controller how you see fit.
So in iOS 7 you will want to:
present the view controller (presentation mode: UIModalPresentationFormSheet)
set view controller superview background color to clear
change frame of view controller as desired (perhaps making it smaller and then centering it in superview)
I got a full screen modal view from UIPresentationFormSheet with just this line:
modalViewController.view.superview.bounds = CGRectMake(0, 0, 1024, 748);
#bdrobert sadly your nice solution does not work anymore on iOS6 - the container keeps the original size even though the new viewcontroller is embedded with the custom size.
You probably need to use the containment API introduced in iOS5, but you need to add a dimmed background on your own, fetching all touch events for that area.
On change of orientation, this code work perfect....
settingViewController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:settingViewController animated:YES completion:nil];
settingViewController.view.superview.frame = CGRectMake(0, 0, 700, 700);
UIInterfaceOrientation currentOrientation = [self getDeviceOrientation];
if(currentOrientation == UIInterfaceOrientationPortrait || currentOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
CGPoint centerPoint = CGPointMake([[UIScreen mainScreen] bounds].size.width/2, [[UIScreen mainScreen] bounds].size.height/2);
settingViewController.view.superview.center = centerPoint;
}
else
{
CGPoint centerPoint = CGPointMake([[UIScreen mainScreen] bounds].size.height/2, [[UIScreen mainScreen] bounds].size.width/2);
settingViewController.view.superview.center = centerPoint;
}
The modern ios13 way:
use popover (formsheet and page ignore preferredContentSize)
then set or override preferredContentSize aka Ed Pilowat & Co
The view size is fixed. You will have to implement things yourself if you want something different.