UIWindow with wrong size when using landscape orientation - ios

I have an empty application and there is no storyboard or xib involved. I want to have a hidden status bar and support only landscape orientation. Again, I wan't to make those changes only within code and don't touch the Info.plist.
Problem
I create a UIWindow with a controller that says the only supported orientation is landscape. In that case my UIWindow is created in the dimension of portrait mode and doesn't rotate. The expected result would be a screen that is completely cyan.
This is my delegate:
#import "AppDelegate.h"
#import "AppViewController.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor cyanColor];
self.window.rootViewController = [[AppViewController alloc] init];
[self.window makeKeyAndVisible];
return YES;
}
#end
This is my controller:
#import "AppViewController.h"
#implementation AppViewController
- (BOOL)shouldAutorotate {
return YES;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeLeft;
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
#end
What I've tried so far
If I set the rootViewController after calling makeKeyAndVisible everything seems to work at first.
self.window.backgroundColor = [UIColor cyanColor];
[self.window makeKeyAndVisible];
self.window.rootViewController = [[AppViewController alloc] init];
There are still some issues. First of all I don't like this since it seems to be very fragile. Second problem is that in a more complex application that sets a GLKViewController as the rootViewController I get the following result (expected would be no black area on the left):
It looks like the status bar is not hidden early enough. Several gesture recognizers are active and in the GLKViewController and clicking on the black area yields the following log message:
2014-09-25 13:20:42.170 StackOverflowExample[6971:107907] unexpected nil window in
_UIApplicationHandleEventFromQueueEvent, _windowServerHitTestWindow: UIClassicWindow: 0x7fa20b805e00; frame = (0 0; 375 667);
userInteractionEnabled = NO; gestureRecognizers = NSArray:
0x7fa20b80a620; layer = UIWindowLayer: 0x7fa20b806890
I also performed various other changes, like attaching an empty UIViewController and adding my view as a sub-view. In that case my view looks correct but the window is still using the wrong dimensions.
Everything rotates correct if I do not override the supportedInterfaceOrientations methods in my view controller. But that is of course not what I want.

When you run landscape app from portrait mode UIScreen has portrait bounds in iOS 8 (only if you haven't this app in app switch panel, as iOS 8 makes some cache). Even displaying window with makeKeyAndVisible doesn't change it's frame. But it changes [UIScreen mainScreen].bounds according to AppViewController avaliable orientation.
#import "AppDelegate.h"
#import "AppViewController.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Portrait bounds at this point
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor cyanColor];
self.window.rootViewController = [[AppViewController alloc] init];
[self.window makeKeyAndVisible];
return YES;
}
#end
So let's change window's frame after [self.window makeKeyAndVisible]
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [UIWindow new];
self.window.backgroundColor = [UIColor cyanColor];
self.window.rootViewController = [[AppViewController alloc] init];
[self.window makeKeyAndVisible];
// Here it is
self.window.frame = [UIScreen mainScreen].bounds;
return YES;
}
I think that it is iOS 8 bug.

I had a similar problem, for a portrait-only app.
I fixed the problem by setting status bar orientation BEFORE instantiate the UIWindow
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Init my stuff
// ...
// Prepare window
[application setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO]; // prevent start orientation bug
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.window makeKeyAndVisible];
return YES;
}
In your case, you should use UIInterfaceOrienationLandscapeLeft (or Right) in the setStatusBarOrientation:animated: method.
Hope it helps you.

Personally, none of the solution presented above worked. I finally set "hidden" to YES for the window in my main xib, as first suggested here: unexpected nil window in _UIApplicationHandleEventFromQueueEvent, _windowServerHitTestWindow

You can rotate UIWindow by adding single line only.
You can set the rootController for your UIWindow. e.g:
fileprivate(set) var bottonOverlayWindow = UIWindow()
self.bottonOverlayWindow.rootViewController = self;
// 'self' will the ViewController on which you had added UIWindow view. So whenever you ViewController change the orientation, your window view also change it's orientation.
Let me know if you face any issue.

The problem is solved when adding a Launch Screen, which you can only do by adding an extra property to the info.plist
had this problem myself, i'm not sure if you can add it through code though, i only managed to make it work with info.plist + Launch Screen xib file
<key>UILaunchStoryboardName</key>
<string>Launch Screen</string>
Actually i don't think you have to add a xib file, if just the key (with any value) is available in the plist it should work.

None of the solutions posted here or elsewhere worked for me.
However, I found that this issue apparently does not occur with Storyboards, so an alternative solution is to move away from xibs. (This fact sadly also makes it unlikely that Apple will take the problem seriously.)

Related

No NIBs, UIWindow Not Filling Screen, iOS 7

I'm avoiding using NIBs completely. I'm getting problems on iOS 7.1, but not on iOS 8.x.
iOS 7 vs. iOS 8
The code looks like this in the UIResponder <UIApplicationDelegate>
- (void) setupViewController {
CGRect frame = UIScreen.mainScreen.bounds;
self.window = [[UIWindow alloc] initWithFrame:frame];
self.window.backgroundColor = UIColor.whiteColor; // viewController's view is green
ViewController *viewController = [[ViewController alloc] init];
UIView *view = [[UIView alloc] initWithFrame:self.window.bounds];
viewController.view = view;
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self setupViewController];
return YES;
}
I've also tried some methods dealing with iOS 8, but even settings the UIWindow frame to 5000, 5000 produces the same results.
[I've seen a number of related questions and tried out the suggestions, but to no avail, including flipping the bounds if it's iOS 8, etc. etc.]
The problem is that you don't have a launch image for the 4-inch screen, so the iPhone 5s is treating this app as a 3.5-inch app (iPhone 4) and letterboxing it.

Why in iOS 8 my app at launch take wrong orientation?

my app up to iOS 7 works correctly. I tried it today with Xcode 6 and when I run it I have a nasty surprise :(:
You can see that Xcode now draw my viewController as if it is in portrait mode but as you can see is in landscape mode.
Do you know what is changed in iOS 8? :/
I used the following orientation methods:
(NSUInteger)supportedInterfaceOrientations
(BOOL)shouldAutorotate
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
EDIT:
I just discover this method:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return UIInterfaceOrientationMaskLandscape;
}
And now my first viewcontrollers works correctly :). the problem is when I show a modal (that change the set of method in UIInterfaceOrientationMaskAll) remake the ugly effect of screenshot.
Precisely i change my methods in this mode:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if(self.restrictRotation)
{
return UIInterfaceOrientationMaskLandscape;
}
else
{
return UIInterfaceOrientationMaskAll;
}
}
// THIS IS A DELEGATE OF MODAL VIEW CONTROLLER
- (void)updateInterfaceAfterDocumentPreviewViewController
{
NSLog(#"updateInterfaceAfterDocumentPreviewViewController");
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft];
AppDelegate* appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
appDelegate.restrictRotation = YES;
}
Please try the following code
In the didFinishLaunchingWithOptions of AppDelegate
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
[self.window setFrame:[[UIScreen mainScreen] bounds]]; //Add
The issue seems to be the order of calls when you set up the window. You need to call makeKeyAndVisible before you assign the rootViewController in your didFinishLaunchingWithOptions method on the app delegate. The following works:
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[self.window makeKeyAndVisible];
self.window.rootViewController = self.myMainViewController;
But if you change the order to:
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = self.myMainViewController;
[self.window makeKeyAndVisible];
You get the behavior you are experiencing.
I had the same exact problem with a "Loading screen". This is what worked for me. (Please note: my App only supports iOS 7 or later, landscape mode.) :
CGRect theFrame = self.view.frame;
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
CGRect screenBounds = [[UIScreen mainScreen] bounds];
theFrame.origin = CGPointZero;
theFrame.size.width = screenBounds.size.height;
theFrame.size.height = screenBounds.size.width;
}
NSLog(#"%#", [NSNumber valueWithCGRect:theFrame]);
self.loadingScreen = [[UIView alloc] initWithFrame:theFrame];
Please refer to Mike Hay's answer if your App supports portrait orientation and for the "long way to calculate the correct applicationFrame":
https://stackoverflow.com/a/18072095/4108485
Hope this helps.
Have a look at your .plist file.
Make sure you have "Initial interface orientation" set as the orientation you wanted.
In the "Supported interface orientations", have the preferred orientation as the first entry on the list (looks like apple changed this order).
I was having a similar problem, I couldn't get the right Launch Images to show (I had two for iPad, Default-Portrait.png and Default-Landscape.png) and I also couldn't get the App itself to auto orient after the Launch Image disappeared.
To solve the problem for the Launch Images:
Go to the Custom iOS Target Properties section of the project (Click on your Project's Icon at the top of the list of project files, and it should be the first section you see).
Add a row to that table called "Launch image" (should autofill) and enter "Default" as the string in the right column.
Make sure your Launch Images follow Apple's filename guidelines (ie: Default-568#2x, Default#2x, etc) check this SO answer
To solve the app orientation issue at first ViewController:
Go to your projects plist file (should be "YourApp-Info.plist")
Add a row and enter "Supported interface orientations"
Add the orientations that your app supports
That approach to detecting orientation was deprecated in iOS8 (but still allowed for now).
It was replaced with the concept of Size Classes and Trait Collections.
(Oddly, I can't seem to find a Size Class Reference in Apple's docs online right now - would swear it was there before - maybe in XCode). See here for WWDC videos.
Anyway, there's a checkbox in Interface Builder that says "Use Size Classes."
Could be you have that checked. Turn it off & see if that helps?
Oldschool solution:
BOOL portrait;
BOOL iOS8OrLater = ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0);
if (iOS8OrLater) {
CGFloat height = CGRectGetHeight(self.view.frame);
CGFloat width = CGRectGetWidth(self.view.frame);
if (width / height > 1.0) {
portrait = NO;
} else {
portrait = YES;
}
} else {
portrait = UIInterfaceOrientationIsPortrait(self.interfaceOrientation);
}
if(portrait) {
[self layoutPortrait];
} else {
[self layoutLandscape];
}
You can call this in viewWillLayoutSubviews
UIInterfaceOrientation currentOrientation = [UIApplication sharedApplication].statusBarOrientation;
if( UIDeviceOrientationIsLandscape( currentOrientation ) )
{
if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad )
{
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
// -- Fix for rotation issue for ios 8
if( IS_IOS_8_OR_LATER )
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPad"
bundle: nil];
// -- Set frame of nav controller
UINavigationController *nc = [storyboard instantiateViewControllerWithIdentifier:#"NavController"];
self.window.rootViewController = nc;
[self.window setFrame:[[UIScreen mainScreen] bounds]];
// -- Set fame of home view controller
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"Home"];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];
[self.window setFrame:[[UIScreen mainScreen] bounds]];
}
}
}

nothing is displayed when testing my app

originally when i tried to test my app i was getting this
[2685:70b] Application windows are expected to have a root view
controller at the end of application launch
and so i changed ccAppDelegate.m
From this
#implementation ccAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
return YES;
}
To this
#import "ccAppDelegate.h"
#import "ccViewController.h"
#implementation ccAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// ... Other code
// Override point for customization after application launch.
UIViewController *viewController = [[UIViewController alloc] init];
self.window.rootViewController = viewController;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
now it is no longer crashing but nothing appears but a white screen
what can i do to fix this?
i have a copy of my work here if anyone wishes to have a look at all the code
https://drive.google.com/file/d/0B3_fSZXQB18lVzZuQVdpT3dnWjQ/edit?usp=sharing
as stated in the comments, you're creating and displaying an empty view controller as the root view controller. As result, you're seeing your brand new empty view controller.
If you want to use your view controller as the root one, you should change the line
UIViewController *viewController = [[UIViewController alloc] init];
to:
MyCustomViewController *viewController = [[MyCustomViewController alloc] init];
where obviously MyCustomViewController should be replaced with the name of your class ( I think it is ccViewController but I'm not sure)
Your method is good but to see anything onscreen you need to add anything rg. UIButton, UIImageView. I assume that you are doing everything in code so if this is hard for you why don't you try Storyboarding.
Anyway, just paste this code and you'll see a red background:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// ... Other code
// Override point for customization after application launch.
UIViewController *viewController = [[UIViewController alloc] init];
self.window.rootViewController = viewController;
self.window.backgroundColor = [UIColor redColor];
[self.window makeKeyAndVisible];
return YES;
}
All I've changed in your code is [UIColor whiteColor] to [UIColor redColor].

Error "Application windows are expected to have a root view controller" (iOS)

I've created a blank iPhone app project and would like to show a full-screen advertisement during app launch.
I tried to install the ad by following this guideline: https://github.com/mopub/mopub-ios-sdk/wiki/Interstitial-Integration-For-iOS
That's what I've done finally:
Actually all codes are just copied from the previous link.
However, an error shows when app runs:
Application windows are expected to have a root view controller at the end of application launch
I think this error may probably related to the loadView method, because if I remove the loadView method, the error disappeared.
In fact, this error seems common as it can be easily searched on the internet, but I don't know how loadView is related to it, and how can it be solved in my case.
Any solutions? Thanks a lot.
You probably need to do this:
Add
#import "ViewController.h"
to the top of AppDelegate.m
And in AppDelegate.m, your application:didFinishLaunchingWithOptions: method should have some code like this.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// ... Other code
// Override point for customization after application launch.
ViewController *viewController = [[ViewController alloc] init];
self.window.rootViewController = viewController;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
UIViewController *vc = [[UIViewController alloc] init];
[vc.view addSubview:self.tab_controller.view];
[self.window setRootViewController:vc];
OR
UIViewController *vc = [[UIViewController alloc] init];
[vc.view addSubview:yourClass.view];
[self.window setRootViewController:vc];
If you started with an empty template and added a storyboard, you need to do a couple of things:
You need to delete all the lines (except return statement) inside didFinishLaunchingWithOptions
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
return YES;
}
In project settings ->General, select your storyboard as the main interface
Attached snapshot to help you
At the right side check there is one option under attribute inspector which asks to set as "is rootView controller"

UIWindow's root view controller does not rotate to landscape at app launch

I am developing a xib-based landscape-only app. The app launches in landscape correctly. However, the view in my main ViewController is presented in portrait. That is, it is rotated 90 degrees so that the image appears cropped and does not take up the entire screen. If I use my interface to present a modal view controller then return to the main ViewController, the issue corrects itself (the view is presented in landscape). This problem did not occur under Xcode 4.2. It occurred after upgrading to Xcode 4.3, and the only code changes that were made were automatically implemented by Xcode when I upgraded the project settings.
Based on advice in other posts, I verified my Info.plist settings for Supported Interface Orientations and Initial Interface Orientation. I overrode the shouldAutorotateToInterfaceOrientation method for each of my view controllers to return YES only for landscape orientations. Also, I turned off auto resizing for the view, as I never want the size/orientation of the view to change.
Based on the ideas in this link [1], I suspected the problem is that the view is not receiving the call to change orientation at launch, possibly due to the removal of the MainWindow.xib concept, which appears to be replaced by the following Xcode-inserted code in AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
I modified this method to generate a generic root view controller from which my ViewController class is presented, as shown in the code below:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ViewController* myViewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.viewController = [[UIViewController alloc] init];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
[self.viewController presentModalViewController:myViewController animated:NO];
return YES;
}
Voila! This solved my problem. However, to me it feels like a change at a fundamental level, which I don't want to make at this point in my development. What I intended to be my root view controller is now a modal view. Does anyone have another solution to this issue?
Thanks in advance!
I had this same issue: an app which was meant to be in Landscape that assumed the ViewController was always in Portrait. I made tons of changes to every aspect of the project and info.plist, including giving the main UIWindow a root-view controller which was landscape. It still didn't work. I eventually undid all the changes and just added the two lines noted below to my app delegate:
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
[_window addSubview:[_viewController view]];
glView = _viewController.glView;
// THIS FIXED ORIENTATION FOR ME IN IOS 6
_window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
// END
...my other setup code here...
}
Nothing else was necessary.
It appears that, for some reason, in iOS 6 the UIWindow root-view-controller setting is sometimes ignored in Interface Builder. I am sure my reasoning is wrong somewhere, but I thought this might well help prod someone in the direction of a fuller, more exhaustive answer.
In iOS 8, settings windows frame to the UIScreen is also necessary, as it won't get automatically updated.
self.window.frame = [UIScreen mainScreen].bounds;

Resources