Setting UIScreen's mode / resolution - ios

I have to set the external UIScreen's mode to run with the resolution 1024x768.
First I search if the screen supports this resolution:
if ([[UIScreen screens] count] > 1){
CGSize size1024;
size1024.height = 0;
size1024.width = 0;
UIScreenMode *screenMode1024 = nil;
UIScreen *secondScreen = [[UIScreen screens] objectAtIndex:1];
for(int i = 0; i < [[secondScreen availableModes] count]; i++)
{
UIScreenMode *current = [[[[UIScreen screens] objectAtIndex:1] availableModes] objectAtIndex: i];
if (current.size.width == 1024.0 && current.size.height == 768.0)
{
size1024 = current.size;
screenMode1024 = current;
break;
}
}
}
After that I set the external screen's mode to use this resolution, but somehow it does not work and the screen is using other, the default resolution.
secondScreen.currentMode = screenMode1024;
UIWindow *secondWindow = [[UIWindow alloc] initWithFrame: CGRectMake(0,0, size1024.width, size1024.height)];
secondWindow.screen = secondScreen;
...
secondWindow.hidden = NO;
Any help ? Maybe I missed some settings ? I also tried with this :
[[[UIScreen screens] objectAtIndex:1] setCurrentMode:screenMode1024];

I found the solution.
The problem was that the screen's mode has to be changed when the external screen is connected to the iOS device.
[[NSNotificationCenter defaultCenter] addObserver: self selector:#selector(screenDidConnectNotification:) name: UIScreenDidConnectNotification object: nil];
The screen mode should be changed inn the screenDidConnectNotification function.

Related

Newly created UIWindow is sideways when app is opened on landscape

I have an iPad application in which I create a new UIWindow at the beginning of the app and show it while I do some resource synchronization. When the app is launched while the iPad is on portrait orientation, everything's fine. However, when on landscape orientation, the newly created UIWindow's size seems fine but it appears sideways and it's coordinates seem all weird. Here are the screenshots for both portrait and landscape orientations:
The landscape one is obtained by rotating to right once.
The piece of code where I create and show the UIWindow is as follows:
UIWindow *window=[UIApplication sharedApplication].keyWindow;
self.progressWindow = [[UIWindow alloc] initWithFrame:window.frame];
self.progressWindow.backgroundColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f];
self.progressWindow.windowLevel = UIWindowLevelAlert;
/* some other code to create the subviews */
[self.progressWindow makeKeyAndVisible];
This problem only occurs on iOS 8.
As far as I understood, newly created UIWindow's do not rotate automatically when the orientation changes. I don't know whether this is new to iOS 8 or if it is a bug, but I was able to overcome the problem with the following piece of code:
UIWindow *window=[UIApplication sharedApplication].keyWindow;
self.progressWindow = [[UIWindow alloc] initWithFrame:window.frame];
self.progressWindow.backgroundColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f];
self.progressWindow.windowLevel = UIWindowLevelAlert;
/* some other code to create the subviews */
[self handleOrientationChange];
[self.progressWindow makeKeyAndVisible];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleOrientationChange) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
Implementation for handleOrientationChange is as follows:
#define DegreesToRadians(degrees) (degrees * M_PI / 180)
- (void)handleOrientationChange
{
if (self.progressWindow)
{
// rotate the UIWindow manually
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
[self.progressWindow setTransform:[self transformForOrientation:orientation]];
// resize the UIWindow according to the new screen size
if ([[UIScreen mainScreen] respondsToSelector:#selector(nativeBounds)])
{
// iOS 8
CGRect screenRect = [UIScreen mainScreen].nativeBounds;
CGRect progressWindowFrame = self.progressWindow.frame;
progressWindowFrame.origin.x = 0;
progressWindowFrame.origin.y = 0;
progressWindowFrame.size.width = screenRect.size.width / [UIScreen mainScreen].nativeScale;
progressWindowFrame.size.height = screenRect.size.height / [UIScreen mainScreen].nativeScale;
self.progressWindow.frame = progressWindowFrame;
}
else
{
// iOs 7 or below
CGRect screenRect = [UIScreen mainScreen].bounds;
CGRect progressWindowFrame = self.progressWindow.frame;
progressWindowFrame.origin.x = 0;
progressWindowFrame.origin.y = 0;
progressWindowFrame.size.width = screenRect.size.width;
progressWindowFrame.size.height = screenRect.size.height;
self.progressWindow.frame = progressWindowFrame;
}
}
}
- (CGAffineTransform)transformForOrientation:(UIInterfaceOrientation)orientation {
switch (orientation) {
case UIInterfaceOrientationLandscapeLeft:
return CGAffineTransformMakeRotation(-DegreesToRadians(90));
case UIInterfaceOrientationLandscapeRight:
return CGAffineTransformMakeRotation(DegreesToRadians(90));
case UIInterfaceOrientationPortraitUpsideDown:
return CGAffineTransformMakeRotation(DegreesToRadians(180));
case UIInterfaceOrientationPortrait:
default:
return CGAffineTransformMakeRotation(DegreesToRadians(0));
}
}
I got the idea from the accepted answer of this question.
The shared application maintains a reference to the window through its
key- Window property:
let w = UIApplication.sharedApplication().keyWindow
That reference, however,
is slightly volatile, because the system can create temporary windows
and interpose them as the application’s key window.
try instead
[[UIApplication sharedApplication].delegate window]

CCScrollView scroll and touch events never firing

I can't find any helpful tutorials or explanation on how to use a CCScrollView. I have a grid-layout of sprites and labels (listing achievements for an iOS game). There are more than can fit on the screen so I want the user to be able to scroll.
To scroll, the user would swipe/pan upwards, to reveal the sprites etc which are lower.
I've found a few code samples and they seem to indicate you just need to add your content node to the scroll node and it will take care of the rest.
It doesn't seem to work. There's no scroll, and the pan/touch events on the scroll layer never seem to fire. The close button I have at the same child (sibling to the scroll view) no longer works as well.
I'm not using SpriteBuilder.
// Node to hold all sprites/labels
scrollContents = [CCNode node];
// I add a bunch of sprites/labels in a grid view
for( NSString *key in badgeKeys ){
// logic to load the sprite would be here
CCSprite *badge = [CCSprite spriteWithSpriteFrame:frame];
badge.positionType = CCPositionTypeNormalized;
badge.position = ccp(xPos,yPos);
[scrollContents addChild:badge];
// some logic to increment x/y position logic, for grid layout
}
// Scroll view
scrollView = [[CCScrollView alloc] initWithContentNode:scrollContents];
scrollView.horizontalScrollEnabled = NO;
scrollView.verticalScrollEnabled = YES;
[scrollView setBounces:NO];
// My sprites never even show unless I manually set this
scrollContents.contentSize = CGSizeMake(self.contentSize.width,960);
NSLog(#"scrollContents contentsize: %f %f", scrollContents.contentSize.width,scrollContents.contentSize.height);
[self addChild:scrollView];
ok, here is a working example (i deconstructed part of my code to give you a fully working code sample) of a scrolling menu with 'live' buttons inside. I just tested this 'deconstruction' , it works
- (void) scrollingMenuWithCharmsTest {
// setup something to scroll
GameInventory *gi = [GameInventory sharedGameInventory];
while (gi.armorCharms.count < 20) {
[gi addArmorCharm:[ArmorCharm createRandomArmorCharm]];
}
CCNode *contentNode = [self charmsContentNodeFor:gi.armorCharms
showEquiped:NO
spacingBetweenMenuItems:8
target:self
selector:#selector(onArmorCharmSelected:)];
// setup a clipping node to crop out the CCScrollingMenu
CCNodeColor *ccn = [CCNodeColor nodeWithColor:[CCColor blackColor] width:180 height:200];
ccn.anchorPoint = ccp(0, 0);
CCClippingNode *cn = [CCClippingNode clippingNodeWithStencil:ccn];
cn.alphaThreshold = 0.05f;
[self addChild:cn];
cn.inverted = NO;
cn.positionInPointsV = ccp(50, 50);
cn.anchorPoint = ccp(0, 0);
cn.contentSizeInPoints = CGSizeMake(180, 200);
// setup scrolling menu
CCScrollView * bsm = [[CCScrollView alloc] initWithContentNode:contentNode];
bsm.contentSize=CGSizeMake(180,200);
[cn addChild:bsm];
bsm.position = ccp(0, 0);
bsm.bounces = YES;
bsm.pagingEnabled = NO;
bsm.verticalScrollEnabled = YES;
bsm.horizontalScrollEnabled = NO;
bsm.contentSizeInPoints = CGSizeMake(180, 200); // inPoints only after the node has a parent
for (CharmAbstractBoxMenuItem *lmi in bsm.contentNode.children) {
TRACE(#"item %# is at %#", lmi.item.description, NSStringFromCGPoint(lmi.positionInPointsV));
}
TRACE(#"number of pages : %i", bsm.numVerticalPages);
}
- (CCNode *)charmsContentNodeFor:(NSDictionary *)keyedItems
showEquiped:(BOOL)isShowEquiped
spacingBetweenMenuItems:(float)inSpacing
target:(id)inTarget
selector:(SEL)inSelector {
NSSortDescriptor *sortOrder = [NSSortDescriptor sortDescriptorWithKey:#"self" ascending:YES];
NSArray *sortedKeys = [[keyedItems allKeys] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortOrder]];
float initialY = 0;
float currentY = initialY;
NSUInteger itemNumber = 0;
CGFloat width = 0;
CGFloat height = 0;
CCNode *contentNode = [CCNode node];
for (NSUInteger loopi = 0; loopi < [sortedKeys count]; loopi++) {
NSString *key = [sortedKeys objectAtIndex:loopi];
CharmAbstract *ci = [keyedItems objectForKey:key];
if (ci) {
CharmAbstractBoxMenuItem *cmi = [CharmAbstractBoxMenuItem itemBoxFor:ci
target:inTarget
selector:inSelector
];
cmi.toolTip = ci.toolTip;
cmi.position = ccp(deviceOffset(0), currentY);
cmi.key = key;
[contentNode addChild:cmi z:0 name:[NSString stringWithFormat:#"%li", (long) itemNumber]];
currentY += cmi.contentSizeInPoints.height + inSpacing;
itemNumber++;
if (cmi.contentSize.width > width) width = cmi.contentSize.width;
height += cmi.contentSize.height;
if (loopi < sortedKeys.count - 1) height += inSpacing;
}
else {
MPLOG(#"*** Key [%#] yielded no items.", key);
}
}
contentNode.contentSizeType = CCSizeTypePoints;
contentNode.contentSize = CGSizeMake(width, height);
return contentNode;
}
some notes :
i gave you my 'build content node' routine so you know the ins and outs of positions and sizes.
my charmBoxMenuItemss derive from 'CCButton' and are hot ... In the full version of this code snippet, i extended CCScrollView to prevent the buttons from being 'hot' outside the crop area (although they are cropped out from view, they are still 'visible' by default, and could respond when a random tap occurs above or below the crop area).
For clipping node with stencil, you need to add this in your setupCocos2dWithOptions line:
CCSetupDepthFormat : [NSNumber numberWithUnsignedInt:GL_DEPTH24_STENCIL8_OES]

Memory increases when lazy loading images in UIScrollView iOS7

My app continues to increase in 'live bytes' by about 600kb every time I scroll to a new image. I am loading my images lazily, meaning only 3 at a time, into a UIScrollView that pages horizontally. I have run Xcode instruments and I believe I have tracked the problem to this line:
newPageView = [[UIImageView alloc] initWithImage:[self.album objectAtIndex:page]];
Every page that gets loaded creates a new UIImageView instance, and for some reason, the space is held in memory. I am using ARC and XCode5. Since there is no way to 'dealloc' objects in ARC, what is the best way to free up this memory?
Here's a snapshot of my instruments showing the images that keep taking up the memory:
I have seen this question proposed in other areas online but could not find an answer that helped. I'm fairly new to coding but very willing to learn, any references would be greatly appreciated! thanks!
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.pageCount = self.album.count;
// Set up the array to hold the views for each page
self.pageViews = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < self.pageCount; ++i) {
[self.pageViews addObject:[NSNull null]];
}
NSInteger p = self.page;
CGFloat w = self.scrollView.bounds.size.width;
[self.scrollView setContentOffset:CGPointMake(p*w,0) animated:YES];
// Set up the content size of the scroll view
CGSize pagesScrollViewSize = self.scrollView.frame.size;
self.scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * self.pageCount, pagesScrollViewSize.height);
// Load the initial set of pages that are on screen
[self loadVisiblePages];
}
- (void)loadVisiblePages {
// First, determine which page is currently visible
CGFloat pageWidth = self.scrollView.frame.size.width;
NSInteger page = (NSInteger)floor((self.scrollView.contentOffset.x * 2.0f + pageWidth) / (pageWidth * 2.0f));
// Keeps track of which image is showing, for passing to child view controller
self.currentImage = [self.album objectAtIndex:page];
self.title =[NSString stringWithFormat:#"%d of %d", [self.album indexOfObject:self.currentImage]+1, self.pageCount];
// Work out which pages we want to load
NSInteger firstPage = page - 1;
NSInteger lastPage = page + 1;
// Purge anything before the first page
for (NSInteger i=0; i<firstPage; i++) {
[self purgePage:i];
}
for (NSInteger i=firstPage; i<=lastPage; i++) {
[self loadPage:i];
}
for (NSInteger i=lastPage+1; i<self.pageCount; i++) {
[self purgePage:i];
}
}
- (void)loadPage:(NSInteger)page {
if (page < 0 || page >= self.pageCount) {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Load an individual page, first seeing if we've already loaded it
UIView *pageView = [self.pageViews objectAtIndex:page];
// create an instance of imageView to be used as the newPageView
UIImageView *newPageView;
if ((NSNull*)pageView == [NSNull null]) {
CGRect frame = self.scrollView.bounds;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0.0f;
newPageView = [[UIImageView alloc] initWithImage:[self.album objectAtIndex:page]];
newPageView.contentMode = UIViewContentModeScaleAspectFit;
newPageView.frame = frame;
[self.scrollView addSubview:newPageView];
[self.pageViews replaceObjectAtIndex:page withObject:newPageView];
}
}
- (void)purgePage:(NSInteger)page {
if (page < 0 || page >= self.pageCount) {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Remove a page from the scroll view and reset the container array
UIView *pageView = [self.pageViews objectAtIndex:page];
if ((NSNull*)pageView != [NSNull null]) {
[pageView removeFromSuperview];
[self.pageViews replaceObjectAtIndex:page withObject:[NSNull null]];
[self.album replaceObjectAtIndex:page withObject:[NSNull null]];
}
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[self loadVisiblePages];
}
ARC tries to release memory automatically so you don't have to worry about doing so.
However, you can kind of 'mark' objects so they can be released by 'niling' them:
yourobject = nil;
this kind of tells the IOS device that the object is not being used and can be released. If you're loading images into a scrollview, that means as the user scrolls down, there is content still in memory that is not on-screen and therefore is not needed. You can get rid of these images and then load them again when the scrollview gets near a certain point.
You might find an answer in this question (which is very similar to yours) helpful: Memory pressure in app
It's difficult to give an answer without see your code, but i suppose when you speak about "Lazy loading", you mean you only add your image to the scrollview when you need. But are you removing the images from the scrollview when they are not displayed?
I don't know which kind of display you want, but take a look on UICollectionView. Collection view manage the memory itself and only display cells when needed and UICollectionViewLayout allows you to customize the collection view.
I've changed my code to resize the images as they are loaded as followed:
- (void)loadPage:(NSInteger)page {
if (page < 0 || page >= self.pageCount) {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Load an individual page, first seeing if we've already loaded it
UIView *pageView = [self.pageViews objectAtIndex:page];
// create an instance of imageView to be used as the newPageView
UIImageView *newPageView;
if ((NSNull*)pageView == [NSNull null]) {
CGRect frame = self.scrollView.bounds;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0.0f;
self.resizeImage = [self.album objectAtIndex:page];
UIImage *theImage = [self resizeImage:self.resizeImage toWidth:320.0f andHeight:480.0f];
newPageView = [[UIImageView alloc] initWithImage:theImage];
newPageView.contentMode = UIViewContentModeScaleAspectFit;
newPageView.frame = frame;
[self.scrollView addSubview:newPageView];
[self.pageViews replaceObjectAtIndex:page withObject:newPageView];
}
}
- (UIImage *)resizeImage:(UIImage *)image toWidth:(float)width andHeight:(float)height {
CGSize newSize = CGSizeMake(width, height);
CGRect newRectangle = CGRectMake(0, 0, width, height);
UIGraphicsBeginImageContext(newSize);
[self.resizeImage drawInRect:newRectangle];
UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resizedImage;
}
This seems to be holding the memory at a stable place with no permanent increase in memory consumption. I am not sure if this is the best way to solve this problem so if anyone has any better ideas, please provide help.

Center Image in ScrollView Xcode

I have a problem with images in a scrollview. I want to scroll the images and they should be centered. There is a problem because each picture has other values in the width.
I tried it with subclassing the scrollview and, but i don't know how to get the actual image in the layoutSubviews-Method.
Status-Update:
Now i try it with scrollViewDidScroll:. But it don't stop on the next image, when i scroll fast.
The method
In arrayWithXValues stands the values where the image begins in scrollview.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGSize iOSDeviceScreenSize = [[UIScreen mainScreen] bounds].size;
float middleOfScreenX = scrollView.contentOffset.x + iOSDeviceScreenSize.width / 2;
for (int i = 1; i < 19; i++) {
if (i < 17) {
if (middleOfScreenX > [[arrayWithXValues objectAtIndex:i] floatValue] && middleOfScreenX < [[arrayWithXValues objectAtIndex:i + 1] floatValue] && actualPage != i) {
...
actualPage = i;
[self killScroll];
}
} else {
if (valMiddleX > [[arrayWithXValues objectAtIndex:i] floatValue]) {
...
actualPage = i;
[self killScroll];
}
}
}
}
- (void)killScroll {
self.horizontalScrollView.scrollEnabled = NO;
self.horizontalScrollView.scrollEnabled = YES;
}
Thanks for all your help

Show full screen on External Display for iOS

I just want to show full screen on External Display.
I'm using the code like this:
- (void) startTvOut {
NSArray *screens = [UIScreen screens];
CGSize max;
max.width = 0;
max.height = 0;
UIScreenMode *maxScreenMode = nil;
for (UIScreen *screen in screens)
{
if (screen != [UIScreen mainScreen])
{
for(int i = 0; i < [[screen availableModes] count]; i++)
{
UIScreenMode *current = [[screen availableModes] objectAtIndex: i];
if (current.size.width > max.width)
{
max = current.size;
maxScreenMode = current;
}
}
if (exWindow == nil)
{
exWindow = [[HUWindow alloc] initWithFrame:screen.brounds];
exWindow.backgroundColor = [UIColor whiteColor];
}
screen.overscanCompensation = UIScreenOverscanCompensationInsetBounds;
screen.currentMode = screen.preferredMode;
exWindow.screen = screen;
[exWindow makeKeyAndVisible];
m_isStarted = YES;
}
}
}
It can't show full screen on external device.
After I change the code
from
screen.overscanCompensation = UIScreenOverscanCompensationInsetBounds;
to
screen.overscanCompensation = UIScreenOverscanCompensationInsetApplicationFrame; it can show full screen, but the point (0,0) is not at the top of left screen.
My goal is to show the screen with full screen and have the point (0, 0) at the upper left corner of the screen. But it doesn't work.
Thanks in advance
Change this line:
screen.overscanCompensation = UIScreenOverscanCompensationInsetBounds;
to
screen.overscanCompensation = 3;
It's a non documented value...
For me the following code solved the problem (XCode 4, iOS 12.1)
screen.overscanCompensation = .scale
Before adding this line, I had the problem, that a black frame was around the screen.

Resources