How do I detect orientation on app launch for splash screen animation on iPad! - iphone

Hi I have an app and I have two *.pngs for default splash screen:
Default-Landscape.png
Default-Portrait.png
What I want is to animate this default splash screen away when my app is loaded and ready to go.
To achieve this I would normally present an UIImageView with either default-landscape or default-portrait (depending on the device orientation), keep it on screen for a certain time and then animate it away.
My problem is that if I call [[UIDevice currentDevice] orientation] in
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
The answer is always that the device is in portrait orientation even if I clearly have it in landscape. I tried this in simulator and on the device as well and the behaviour is the same.
Does anyone know a fix for this or maybe some other approach?
Thanks!

I had troubles with this and I solved it by making one image 1024x1024 and setting the contentMode of the UIImageView to UIViewContentModeTop, then using left and right margin autoresizing. So long as your portrait and landscape default images are the same layout then this will work fine.
Just to clarify here's what I used:
bgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:SplashImage]];
bgView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
bgView.contentMode = UIViewContentModeTop;

To get around this problem I installed the splash image view inside of a view controller that allowed both orientations. Even though the device reported the wrong orientation at startup, the view controller seems to get the right one.

You can use UIApplication statusBarOrientation as follows:
if ( UIDeviceOrientationIsLandscape( [[UIApplication sharedApplication] statusBarOrientation] ))
{
// landscape code
}
else
{
// portrait code
}

Maybe you could show a blank view with black background at start time and place [[UIDevice currentDevice] orientation] into this view's viewDidAppear and start your splash screen from there?

Another solution would be to read the accelerometer data and determine the orientation yourself.

To know at start what is the orientation (UIDevice orientation don't work until user have rotate the device) intercept shouldAutorotateToInterfaceOrientation of your View Controller, it is called at start, and you know your device orientation.

There are certainly times when you want to transition from the loading image to something else before the user gets control of your app. Unless your app is really simple, going from loading image to landing page probably won't be sufficient without making the app experience really suck. If you ever develop a large app, you'll definitely want to do that to show progress during setup, loading xibs, etc. If an app takes several seconds to prepare with no feedback, users will hate it. IMO, there's nothing wrong with a nice transition effect either. Almost nobody uses loading screens the way Apple suggests to. I don't know which apps you looked at that showed the "empty UI" type loading screens they suggest. Heck, even Apple doesn't do that except in their sample code and none of my clients would find that acceptable. It's a lame design.

Only the first view added to the window is rotated by the OS. So if you want your splash screen to automatically rotate AND your main view is rotatable then just add it as a child of that view.
Here is my code for fading out the appropriate splash screen image:
// Determine which launch image file
NSString * launchImageName = #"Default.png";
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
if (UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation])) {
launchImageName = #"Default-Portrait.png";
} else {
launchImageName = #"Default-Landscape.png";
}
}
// Create a fade out effect
UIImageView* whiteoutView = [[[UIImageView alloc] initWithFrame:self.window.frame] autorelease];
whiteoutView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
whiteoutView.autoresizesSubviews = YES;
whiteoutView.image = [UIImage imageNamed:launchImageName];
[[[self.window subviews] objectAtIndex:0] addSubview:whiteoutView];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
whiteoutView.alpha = 0.0;
[UIView commitAnimations];
Note: You'll have to update it to support hi-res screens.

It sounds like you're not using the launch image the way Apple recommends in the iOS HIG. It specifically calls out that you should not use it as a splash screen, but rather as a skeleton version of your actual UI. The net effect is that your app appears to be ready just that much faster.
The suggestions that you could draw a splash screen yourself after the app has launching in viewDidAppear or similar also are missing the basic purpose of a launch image. It's not a splash screen. If your app is ready, let the user interact with it, don't waste their time drawing a splash screen.
From my five minute survey of Apple apps and third-party apps, everyone showed a portrait launch image, loaded the portrait version of the UI, and then rotated to landscape. It's been a while since programming on iOS, but I think this mirrors the order of the method calls -- first your app gets launched, then it is told to rotate to a particular orientation.
It might be a nice enhancement request to file with Apple though :)

Related

2 XIB and 1 viewcontroller but functions don't work in the ipad XIB

I believe that I did everything necessary to change my app for ipad (was for iphone at start). I can toggle the build status to either iphone (only), ipad (only) or iphone/ipad - and the app launches either in ipad or iphone simulator. I can do that forth and back at will.
I added the idiom to check for ipad and basically for one of my xib, instead of using the string of my xib to create the controller, I use the one for the ipad. So it is a new xib for ipad with all same graphical objects ( enlarged ;-) ) . I added the callbacks to function correctly with IB.
I can see everything fine and arrive on my new ipad view BUT when I click on one of my buttons... nothing happened like if my callbacks don't work. It is very surprising and actually I have no idea where to look as I compared most of the parameters between my iphone and ipad view and they are identical as far as I can see.
It must be something damn obvious so if one of you had the same issue and it was a very simple answer ... I guess that would be what I missed!
Thanks for your help in advance
Cheers,
geebee
EDIT1: Some code as requested
at start I have that to decide either way:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
examTFVC_NIB=#"ExamTFViewController-iPad";
}
else
{
examTFVC_NIB=#"ExamTFViewController";
}
Then to go to the right view:
ExamTFViewController *examTFViewController = [[ExamTFViewController alloc]
initWithNibName:globals.examTFVC_NIB bundle:nil];
But I have no problem loading the correct XIB. The issue is really the callback functions not being called...
Thanks for the help.
EDIT2:
I also realised that calling the extension of the xib xxx~ipad allows to avoid the example code above. And it works - but still no function can be called.
EDIT3:
IMPORTANT FINDING: if I move my buttons higher and on the left of the screen: they work! So it seems that the functions are called if the event are in the region of an iphone screen although I am on an ipad screen. I guess know it would be more obvious to find the issue! thanks for any help – geebee just now
ANSWER
iPad touch detected only in 320x480 region
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// to correct region size
CGRect rect = [[UIScreen mainScreen] bounds];
[window setFrame:rect];
// now, display your app
[window addSubview:rootController.view];
[window makeKeyAndVisible];
}
** OR OTHER SOLUTION **
Check the full screen at launch checkbox - only present in the ipad xib MainWindow
finally solved - with 2 methods - programmatically or via IB - in the edited section of the post.

Landscape/Portrait conflict - iOS

Just when i thought I had everything figured out .. i got this problem.
the scenario.
I got a simple tableView. and with a search bar in navigation item's titleView. The SearchBar is added to navItems titleView via a uibarbuttonitem in view controllers toolbar.
NOW, normally
After initiating the searchbar with [beginResponder] the keyboard shows up. And It sends out a notification "KeyboardDidShow" which is where i calculate the UIKeyboard's height and set the tableView's height accordingly (Shorten it).
ON Rotation - to and fro landscape/portrait, everything works fine.
-(void)didRotateInferfaceOrientation is called and everythings kool.
Heres the problem.
When the keyboard is active, it has a Google "search" button, this pushes to a new view - webviewcontroller.
the problem is, this
When, [PORTRAIT]ViewController [SearchBar with keyboard active] --> taps Search --> [Portrait]WebViewController --> Change Device Orientation to [Landscape] --> [Landscape]WebViewController changes to landscape ---> HERES THE PROBLEM, user taps back to uiViewController[Landscape]
the method -didRotatefromInterfaceOrientation isnt called. and somehow the tableView height is messed up. Though the view is rotating perfectly.
Is there something im missing here..
would appreciate any help. .thanks
When user taps back, -didRotatefromInterfaceOrientation will not be called. You need to check orientation in viewWillAppear (or call viewDidLoad, prior to returning from tap on back), and then call the proper layout for the chosen orientation.
In all of your (BOOL)shouldRotate... methods, you should be call a separate method to ensure your layout is correct for the device orientation.
I got a similar problem in one of my applications recently, not exactly our problem but don't bother, you should see what I'm heading for: I wanted to simply rotate an viewController displayed using presentModalViewController...Unfortunatly it didn't really worked put, especially on old iPhone with OS prior to iOS 4...So I needed to rotate programatically! Just get your screen size, use CGAffineTransform or something like that and change the sizes and then you should be done...
If your interested I could post a bunch of code, so let me know!
EDIT:
UIScreen *screen = [UIScreen mainScreen];
myController.view.bounds = CGRectMake(0, 0, screen.bounds.size.height, screen.bounds.size.width - 20);
if(currentOrientation == UIDeviceOrientationLandscapeRight){
myController.view.transform = CGAffineTransformConcat(myController.view.transform, CGAffineTransformMakeRotation(degreesToRadian(-90)));
}else{
myController.view.transform = CGAffineTransformConcat(myController.view.transform, CGAffineTransformMakeRotation(degreesToRadian(90)));
}
myController.view.center = window.center;
[[UIApplication sharedApplication] setStatusBarOrientation:currentOrientation];
[self.window addSubview:self.tabBarController.view];
[self.window bringSubviewToFront:self.tabBarController.view];
[self.window addSubview:myController.view];
[self.window bringSubviewToFront:myController.view];
[self.tabBarController.view removeFromSuperview];`
This also includes removing a TabBar when rotating to landscape to get some more space...enjoy :)
You could call didRotateFromInterfaceOrientation manually on viewWillAppear and just pass an orientation yourself (i.e. [UIApplication sharedApplication].statusBarOrientation).

Launching application in landscape orientation for IPad

Facing one issue with launching application in landscape orientation for IPad.
I have developed IPhone application which later I ported to IPad.
I have made setting regarding orientation in info.plist
[ UISupportedInterfaceOrientations~ipad ] to support all orientation UIInterfaceOrientationPortrait , UIInterfaceOrientationPortraitUpsideDown , UIInterfaceOrientationLandscapeLeft , UIInterfaceOrientationLandscapeRight.
but when I start IPad application in the landscape mode, it always start in the potrait mode.
Along this
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{ return YES; }
help me, if I am missing something with this..
Thanks,
Sagar
here's something I also discovered: setting the initial interface orientation in your info.plist is being ignored if you have Supported interface orientations set up with another orientation in the first slot! Put your initial orientation there as well - and the simulator will launch correctly, as will the app. this drove me nuts for a long time!
Put UISupportedInterfaceOrientations into your -Info.plist, with a setting for each orientation you support. This is used to see which orientation the app can start in. From there onwards it will ask your view controllers.
Sagar - I had the same issue but was able to resolve it.
Like yours, my app started as an iPhone app which I "upgraded" to a Universal app using the XCode wizard. I noticed that when running on the actual iPad, starting in landscape, the app would start in Portrait, then maybe rotate to Landscape. On the simulator, starting in landscape, the app would start in Landscape, then the simulator would rotate to Portrait.
On the iPad, my app is a split-view app with TabBarControllers on left and right. Each tab is a view controller that returns YES to shouldAutoRotateToInterfaceOrientation.
I noticed that a brand-new wizard-generated, simple-case with a splitviewcontroller, Universal app didn't have this problem.
The difference I found between my app and the simple-case was that I wasn't adding my splitview-controller's view to the app window in applicationDidFinishLaunchingWithOptions. Instead I was showing a "loading" view at this stage, then later when an initialization thread completed I'd add my splitviewcontroller's view (and hide the "loading" view).
When I added my splitviewcontroller's view to the app window during the call to applicationDidFinishLaunchingWithOptions everything started working fine.
There must be some magic that happens on return from applicationDidFinishLaunchingWithOptions???
Is your app similar to mine in that it isn't adding the main view controller's view to the window during applicationDidFinishLaunchingWithOptions?
As pointed out in a number of posts, you must set up the info.plist with both the supported and the initial interface orientations. However, the bigger issue is when does the initial orientation become effective? The answer is NOT when your view controller receives the "viewDidLoad" message. I found that on the iPad-1, running iOS 5.0, the requested initial orientation becomes effective only after several "shouldAutorotateToInterfaceOrientation"
messages are received.(This message passes the UIInterfaceOrientation parameter to the receiver.) Furthermore, even if the orientation says it is in Landscape mode, it may not be! The only way I found to be sure that the view is in Landscape mode is to test that the view height is less than the view width.
The strategy that worked for me was to lay out the subViews I wanted when the "viewDidLoad" message was received but to delay actually adding those subViews to the view until the controller received a valid "shouldAutorotate.." message with the orientation set to Landscape mode. The code looks something like:
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
// N.B. Even when the interface orientation indicates landscape mode
// this may not really be true. So we insure this is so by testing
// that the height of the view is less than the width
if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
CGRect viewBounds = [[self view] bounds];
if ( viewBounds.size.height < viewBounds.size.width )
[self addMySubViews];
return YES;
}
else
return NO;
}
Apple has just released iOS 5.1, so this behavior may have changed. But I expect the code that is here should still work.

iPhone OS: Rotate just the button images, not the views

I am developing an iPad application which is basically a big drawing canvas with a couple of button at the side. (Doesn't sound very original, does it? :P)
No matter how the user holds the device, the canvas should remain in place and should not be rotated. The simplest way to achieve this would be to support just one orientation.
However, I would like the images on the buttons to rotate (like in the iPhone camera app) when the device is rotated. UIPopoverControllers should also use the users current orientation (and not appear sideways).
What is the best way to achieve this?
(I figured I could rotate the canvas back into place with an affineTransform, but I don't think it is ideal.)
Thanks in advance!
Just spouting off an idea (not sure if it would work or not)...
Perhaps you could have your screen controlled by a UIViewController that supports all orientations, but have the canvas be controlled by one that only supports a single orientation (ie, returns NO in its shouldAutorotate... method).
If that doesn't work, I'd probably just go with the affineTransform route.
I discovered a way to do this, similar to what Dave DeLong proposed.
Using a transform worked, but it wasn't ideal. Although the end result (end of the animation) was what I wanted, it would stay show some kind of shaky rotation animation.
Then I found this:
https://developer.apple.com/iphone/library/qa/qa2010/qa1688.html
Which says that a second (or third etc.) UIViewController added to the WINDOW would not receive rotation events, and therefore would never rotate. And that worked!
I created a 'fake' UIViewController with a blank view and added that as the first view controller. This receives the rotation events which I then pass on to the other view controllers that can then choose whether to rotate - the entire view or just button labels.
It is a bit hacky... But I guess the user won't notice.
Using the willRotateToInterfaceOrientation method, you should be able to use some simple logic to swap out the images:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
//The following if statement determines if it is an iPad, if it is then the interface orientation is allowed. This line can be taken out to support both iPhones an iPads.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
//Swap images and use animations to make the swap look "smooth"
//NSLog(#"Landscape Right");
} else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
//Swap images and use animations to make the swap look "smooth"
//NSLog(#"Landscape Left");
} else {
//Swap images and use animations to make the swap look "smooth"
//NSLog(#"Portrait");
}
} else {
}
}
To change an image in your interface programmatically:
[myUIImageView setImage:[UIImage imageNamed:#"myImage.png"]];
Also, to make sure the view doesn't auto-rotate use the shouldAutorotateToInterfaceOrientation method to tell your app to stay in portrait.

Starting Three20 sample project in landscape mode renders half the screen unresponsive to touch events

I am interested in developing a Three20 based application for iPhone OS that is primarily landscape oriented.
I downloaded the Three20 sample project templates, loaded them in Xcode, and started the sample project app fine in the simulator and on the device using the 3.0, 3.1, 3.1.2 SDK. When I changed the Initial interface orientation to Landscape (right home button) in the project's Info.plist file, the project started in that orientation as expected, but only responded to touch events on left-hand side of the screen. It appears as if it is still responding to touch events as if it is still in portrait mode. The only change I've made to the sample project is changing the Info.plist file.
Am I missing something very simple? Starting in landscape orientation seems to be a very basic use case - but I can't find anyone else who's filed an issue report or blog post on it after googling around for a couple of days.
Note: This is a problem I encountered originally in a much more advanced project while introducing landscape orientation, but I backed up to the most basic repeatable example to rule out any other code as the source of the problem.
Make sure that all views have the correct autoresizingMaskproperty set. As most views do not clip to bounds, content is also visible beyond the bounds of the parentView. Touches however are alway clipped to bounds, which could lead to childViews being visible out side of the bounds of the parentView, but not being touchable.
Ergo: if a parent view does not fit the new width, the right part of the screen might be rendered with content that is not touchable.
Are you using a specific view? It could be as simple as needing to add:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
To your view controller.
- (void)viewWillAppear:(BOOL)animated
{
if (![System isIpad])
{
if (([[UIDevice currentDevice] orientation] == UIInterfaceOrientationPortrait) ||
([[UIDevice currentDevice] orientation] == UIInterfaceOrientationPortraitUpsideDown))
{
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
}
}
[super viewWillAppear:animated];
// self.navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
// self.navigationBarTintColor = [UIColor clearColor];
self.navigationController.navigationBarHidden = YES;
[[UIApplication sharedApplication] setStatusBarHidden:FALSE];
}