I have an iPad app that I would like to work in the sideways orientation instead of just portrait. I have programatically placed images, labels, and buttons into my view and used CGRectMake (x,x,x,x) to tell them where to go on the view into the center. When the app rotates horizontally, I need my labels and buttons to shift up (since they can't go down as far when in landscape mode), but stay in the center. Here is some code I've been playing with:
if((self.interfaceOrientation == UIDeviceOrientationLandscapeLeft) || (self.interfaceOrientation == UIDeviceOrientationLandscapeRight))
{
lblDate = [[UILabel alloc] initWithFrame:CGRectMake(384-(fieldWidth/2)-30,controlTop+45,120,40)]; //these dimensions aren't correct, though they don't matter here
lblDate.text = #"Date:";
lblDate.backgroundColor = [UIColor clearColor];
[contentView addSubview:lblDate];
} else {
//the orientation must be portrait or portrait upside down, so put duplicate the above code and change the pixel dimensions
}
Thanks for your help!
Take a look at this: iphone/ipad orientation handling
You just specify each control location depending on the rotation.
I know this might be a bit of an old question now looking to the date, but I just very recently faced the same problem. You could stumble upon many suggestions such as transforming main view's subviews or it's layers. Non of this worked for me.
Actually the solitary solution I've found is that since you want your UI controls to be located dynamically then don't deploy them mainly in the interface builder. The interface builder can be helpful knowing the desired locations for dynamic controls in both portrait and landscape orientations. i.e make two separate test views in the interface builder, one portrait and the other landscape, align your controls as you wish and right down X, Y, Width and Height data just to use with CGRectMake for each control.
As soon as you write down all needed positioning data from the interface builder get rid of those already drawn controls and outlets/actions links. They will be of no need now.
Of course don't forget to implement UIViewController's willRotateToInterfaceOrientation to set control's frame with each orientation change.
#interface
//Declare your UI control as a property of class.
#property (strong, nonatomic) UITableView *myTable;
#end
#implementation
// Synthesise it
#synthesize myTable
- (void)viewDidLoad
{
[super viewDidLoad];
// Check to init for current orientation, don't use [UIDevice currentDevice].orientation
if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft || self.interfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
myTable = [[UITableView alloc] initWithFrame:CGRectMake(20, 20, 228, 312)];
}
else if (self.interfaceOrientation == UIInterfaceOrientationPortrait)
{
myTable = [[UITableView alloc] initWithFrame:CGRectMake(78, 801, 307, 183)];
}
}
myTable.delegate = self;
myTable.dataSource = self;
[self.view addSubview:myTable];
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight || toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft)
{
// Show landscape
myTable.frame = CGRectMake(20, 20, 228, 312);
}
else
{
// Show portrait
myTable.frame = CGRectMake(78, 801, 307, 183);
}
}
Related
I've been looking at the same problem for so long I'm probably missing a simple solution here.
I created a small library to provide a custom UIView that sticks to the keyboard like the one for iMessage does (aka doesn't hide with keyboard): https://github.com/oseparovic/MessageComposerView
Basically the problem I'm experiencing is that when the user init's custom view I want a view with the following default rect initialized:
CGFloat defaultHeight = 44.0;
CGRect frame = CGRectMake(0,
[self currentScreenSize].height-defaultHeight,
[self currentScreenSize].width,
defaultHeight)
This requires that the currentScreenSize is calculated within the UIView. I've tried multiple implementations all of which have their downsides. There doesn't seems to be a good solution due to this breaking principles of MVC.
There are lots of duplicate questions on SO but most assume you have access to the rest of the code base (e.g. the app delegate) which this custom view does not so I'm looking for a self contained solution.
Here are the two leading implementations I'm using:
NextResponder
This solution seems to be fairly successful in a wide variety of scenarios. All it does is get the next responder's frame which very conveniently doesn't include the nav or status bar and can be used to position the UIView at the bottom of the screen.
The main problem is that self.nextResponder within the UIView is nil at the point of initialization, meaning it can't be used (at least not that I know) to set up the initial frame. Once the view has been initialized and added as a subview though this seems to work like a charm for various repositioning uses.
- (CGSize)currentScreenSize {
// return the screen size with respect to the orientation
return ((UIView*)self.nextResponder).frame.size;
}
ApplicationFrame
This was the solution I was using for a long time but it's far more bulky and has several problems. First of all, by using the applicationFrame you have to deal with the nav bar height as it will otherwise offset the position of your view. This means you have to determine if it is visible, get its height and subtract it from your currentSize.
Getting the nav bar unfortunately means you need to access the UINavigationController which is not nearly as simple as accessing the UIViewController. The best solution I've had so far is the below included currentNavigationBarHeight. I recently found an issue though where this will fail to get the nav bar height if a UIAlertView is present as [UIApplication sharedApplication].keyWindow.rootViewController will evaluate to _UIAlertShimPresentingViewController
- (CGSize)currentScreenSize {
// there are a few problems with this implementation. Namely nav bar height
// especially was unreliable. For example when UIAlertView height was present
// we couldn't properly determine the nav bar height. The above method appears to be
// working more consistently. If it doesn't work for you try this method below instead.
return [self currentScreenSizeInInterfaceOrientation:[self currentInterfaceOrientation]];
}
- (CGSize)currentScreenSizeInInterfaceOrientation:(UIInterfaceOrientation)orientation {
// http://stackoverflow.com/a/7905540/740474
// get the size of the application frame (screensize - status bar height)
CGSize size = [UIScreen mainScreen].applicationFrame.size;
// if the orientation at this point is landscape but it hasn't fully rotated yet use landscape size instead.
// handling differs between iOS 7 && 8 so need to check if size is properly configured or not. On
// iOS 7 height will still be greater than width in landscape without this call but on iOS 8
// it won't
if (UIInterfaceOrientationIsLandscape(orientation) && size.height > size.width) {
size = CGSizeMake(size.height, size.width);
}
// subtract the height of the navigation bar from the screen height
size.height -= [self currentNavigationBarHeight];
return size;
}
- (UIInterfaceOrientation)currentInterfaceOrientation {
// Returns the orientation of the Interface NOT the Device. The two do not happen in exact unison so
// this point is important.
return [UIApplication sharedApplication].statusBarOrientation;
}
- (CGFloat)currentNavigationBarHeight {
// TODO this will fail to get the correct height when a UIAlertView is present
id nav = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([nav isKindOfClass:[UINavigationController class]]) {
UINavigationController *navc = (UINavigationController *) nav;
if(navc.navigationBarHidden) {
return 0;
} else {
return navc.navigationBar.frame.size.height;
}
}
return 0;
}
Does anyone have suggestion about how I can best calculate the UIViewController size from within this UIView. I'm totally open to other suggestions on how to stick the UIView to the bottom of the screen upon initialization that I may have overlooked. Thank you!
+ (id) getCurrentUIViewController : (id)res {
if([res isKindOfClass:[UIViewController class]]) {
return res;
}
else if ([res isKindOfClass:[UIView class]]) {
return [Function getCurrentUIViewController:[res nextResponder]];
}
else {
return nil;
}
}
In my app i trying create a Interface builder supports both Landscape and portrait in ipad and iPhone.
[In Android we used fill parent to autoresize dynamically created UI-Elements.is there any syntax in iOS to autoresizing]
How to UI-elements create dynamically supports both Landscape mode and portrait mode?
How create the view controller to support the Landscape mode and portrait mode?
Is there required to create a all views and UI-elements dynamically?
1)If you will make xib or nib than develop xib or nib in only one mode as portrait or landscape. Than use Autoresizing option as below Image for any control.
http://www.raywenderlich.com/50319/beginning-auto-layout-tutorial-in-ios-7-part-2. You can use this link for auto layout.
But Auto layout is not work properly as you want. so u need to set frames of control pro grammatically evenif u r using autolayout.
2) And If you want to develop dynamically than using below code you can set frame of all controls.
In ViewwillAppear.
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(orientationChanged) name:UIDeviceOrientationDidChangeNotification object:nil];
in viewdidload
set your controls as below.
UILabel lbl - [UILabel alloc]init];
-(void)orientationChanged{
if(Orientation is portrait){
[lbl setFrame:for portrait];
}else{
[lbl setFrame: for landscape];
}
If device change mode than above notification fire and in that method. you can set frame of control.
I hope you will get your answer.
You can use auto - layout for providing both the portrait and landscape mode.
For more details, check this : What is Auto Layout?.
You have to set the constraints for landscape and portrait mode to work. Like if you want a button at the top, you can set constraints on it : from top and left and so on.
If you want a UI element to work change dynamically, you just need to change frame on orientation as per your requirement. Sample code is here :
# pragma mark - Orientation related methods
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration NS_AVAILABLE_IOS(3_0)
{
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
[self deviceRotatedToLandscapeMode];
}
else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
[self deviceRotatedToLandscapeMode];
}
else if (toInterfaceOrientation == UIInterfaceOrientationPortrait) {
[self deviceRotatedToPortraitMode];
}
else if (toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
[self deviceRotatedToPortraitMode];
}
}
- (void) deviceRotatedToPortraitMode {
self.mTableView.frame = CGRectMake(0.0, 0.0, self.view.frame.size.width, self.view.frame.size.height);
}
- (void) deviceRotatedToLandscapeMode {
self.mTableView.frame = CGRectMake(0.0, 0.0, self.view.frame.size.height, self.view.frame.size.height);
}
The most reliable approach -
Create a method in your view controller -
-(void)setFrameForOrientationChange:(UIDeviceOrientation*) o {
//...
implement your code for frame settings..
}
I am building an app that displays an image in landscape and portrait modes. Rotating works perfectly. The image is also perfectly positioned in landscape mode. However it keeps its landscape coordinates in portrait, which misplace it as a result. Please find my code below. Could you let me know what I'm missing? Is there also a way to achieve this strictly from a Xib file?
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[[self navigationController] setNavigationBarHidden:YES animated:NO];
UIImage *startImage = [UIImage imageNamed:#"title.png"];
UIImageView *startImageView = [[UIImageView alloc] initWithImage:startImage];
if (curOrientation == UIInterfaceOrientationPortrait || curOrientation == UIInterfaceOrientationPortraitUpsideDown) {
[startImageView setFrame:CGRectMake(-128, 0, 1024, 1024)];
}else{
[startImageView setFrame:CGRectMake(0, -128, 1024, 1024)];
}
[self.view addSubview:startImageView];
}
Currently you are only calling this code when the view is first loaded. You actually need to call it
whenever the view appears onscreen (in case the device was rotated while it was offscreen)
whenever the device is rotated
but you should keep the view creation code in viewDidLoad, as you only want to create it once.
Make a property to keep a pointer to the view so that you can refer to it from all of these places in your codeā¦
#property (nonatomic, weak) UIImageView* startImageView;
Create it in viewDidLoad (but don't worry then about the geometry, as you can do this in viewWillAppear):
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[[self navigationController] setNavigationBarHidden:YES animated:NO];
UIImage *startImage = [UIImage imageNamed:#"title.png"];
UIImageView *startImageView = [[UIImageView alloc] initWithImage:startImage];
self.startImageView = startImageView;
[self.view addSubview:startImageView];
}
Make a generic orientation method:
- (void) orientStartImageView
{
UIInterfaceOrientation curOrientation = [UIApplication sharedApplication].statusBarOrientation;
if (curOrientation == UIInterfaceOrientationPortrait || curOrientation == UIInterfaceOrientationPortraitUpsideDown) {
[self.startImageView setFrame:CGRectMake(-128, 0, 1200, 1200)];
}else{
[self.startImageView setFrame:CGRectMake(0, -128, 1200, 1200)];
}
}
Call it from viewWillAppear (triggered every time the view comes onscreen):
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self orientStartImageView];
}
Call it from viewWillLayoutSubviews (triggered every time the view IS onscreen and the device rotates):
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
[self orientStartImageView];
}
By the way, I am not sure your frames are correct - in portrait you are shifting the left edge offscreen, in landscape you are shifting the top edge offscreen. Is that what you want? It may well be that you can achieve what you want in Interface Builder, but it is not clear from your code what that is - maybe you could post a picture. Also check that you have Autolayout disabled (checkbox in Interface Builder's file inspector) to simplify issues.
update
You may be able to do this from the Xib with no code: centre the imageView in it's superView, set it's size to your final size (eg 1200x1200), disable Autolayout, deselect all springs and struts, set your View Mode appropriately (eg center or scaleToFill)
Can I use interface builder to my fields different positions in landscape mode and portrait mode ? (Completely different, so I can't just use the layout properties) ?
Or is the code the only way to go ?
thanks
You can use willRotateToInterfaceOrientation method. When you change device orientation it will call..
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
-(void)willRotateToInterfaceOrientation: (UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
{
if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation))
{
[label setFrame:CGRectMake(20, 52, 728, 617)];
}
else
{
[label setFrame:CGRectMake(20, 52, 728, 617)];
}
}
i would say go for the code if the fields shared by portrait and landscape mode are same. In case of having different objects in each mode wont be a good idea.
You can keep two UIViews in interface builder, and when user rotate device, you can hide one and show another based on orientation. Can you please try the following lines of code?
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if(([self.navigationController.visibleViewController interfaceOrientation] == UIDeviceOrientationLandscapeLeft) || ([self.navigationController.visibleViewController interfaceOrientation] == UIDeviceOrientationLandscapeRight)){
self.viewLandscape.hidden = NO;
self.viewPortrait.hidden = YES;
}
else {
self.viewLandscape.hidden = YES;
self.viewPortrait.hidden = NO;
}
}
Here are the approaches that you can use. Best approach is on the top
1. It's better to use auto layout for adjusting your views.
2. Auto layout + code
3. Code only.
4. You can make two views for your xib one for landscape and one for portrait. And show and hide as per the orientation. But in this you need to sync all your portrait views with landscape views (properties like text) and vice versa. This is easy to maintain but you have to take extra headache for syncing the properties of each view.
Hopefully somebody can point out what I'm doing wrong with my Splash screen here. The problem is that the screen is displaying in portrait mode, not landscape...
- (void)showSplash
{
UIView *modelView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024 , 748)];
UIViewController *modalViewController = [[UIViewController alloc] init];
[modelView setBackgroundColor:[[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"Splash.png"]]];
modalViewController.view = modelView;
[self presentModalViewController:modalViewController animated:NO];
[self performSelector:#selector(hideSplash) withObject:nil afterDelay:5.0];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
Thanks in advance for the help.
Looks like you are writing app for iPad. If so, you have to support both landscape as well as portrait orientation otherwise Apple will reject it. I would suggest that you should use two different images. Image specifications are as follows:
Default-Landscape.png (1004 * 768)
Default-Portrait.png (748*1024)
(I am assuming that you are showing status bar if not add 20 pixels to height of an image)
That's it, create these images and add it to your project. And you are good to go. No Need to write any additional piece of code too..
And ya make it
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
You shouldn't rely on a function in your code to display the splash screen. Just define them as the previous answer from Sumit Lonkar explains.
If you do it in code, I believe at the start of the application the orientation is always considered as portrait, then the transition to the actual orientation is triggered. This explains why your code displays first as portrait and most likely there is nothing else in the code to handle rotation. Besides, the purpose of the splash screen is to display something while the app is loading, so if you put it in code you lose the purpose.
By doing it the Apple way you leave it to another Apple process that runs before looking at your code and it will work.
Regarding the orientation supported I have on my iPad some apps that support only landscape (TapZoo for example) so it should be ok with Apple.