My aim was to get the application functioning in both landscape and portrait mode, and all I could figure out to do it was this code below. The app was working fine in portrait, but when switched to landscape, the text wouldn't expand (to the right) to fill up the additional space. I made sure my springs/struts where set, and that the parents had "allowResizing" selected in IB.
Here's what I've done instead:
- (void) willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation duration:
(NSTimeInterval)duration {
UIInterfaceOrientation toInterfaceOrientation = self.interfaceOrientation;
if (toInterfaceOrientation == UIInterfaceOrientationPortrait) {
self.myView.frame = CGRectMake(0.0, 0.0, 320.0, 480.0);
}
else {
self.myView.frame = CGRectMake(0.0, 0.0, 480.0, 256.0);
}
}
Note that it looks just fine in portrait mode (toolbar appears):
Portrait http://mr-sk.com/img/land.png
But the toolbar is gone in landscape mode:
Landscape http://mr-sk.com/img/por.png
Any ideas?
If you use Interface Builder - you get this same result if you don't specify constraints in the object inspector to pin the toolbar to both edges and the bottom (click on the little red lines to specify constraints).
You can also do the same in code - you need to lookup how to do this (but its easier in IB)
There are a few reasons why it could be messing up... maybe your UIToolbar has the wrong parent. Maybe a layoutSubviews is being run and moving it somewhere strange. Or something else.
I recommend you implement a didRotateFromInterfaceOrientation: on your view controller and read the frame of the UIToolbar after the rotation to see where it has gone. This will be the best way to discover the exact problem.
Related
I am facing one issue with hiding the UITabBar when moving to landscape view.
When I am running my app in Xcode 4.6.1 and iOS 6.1 simulator everything works fine but while running the app in Xcode 5 and iOS 7 simulator I face the following issue.
I am using a tabBarController and 3 tabs are located in it at the bottom of screen.
One of the tabs has the functionality to switch from portrait view to landscape view.
We need to show the tabBars in portrait view but need to hide them in landscape view.
When switching the simulator from portrait to landscape view, tabBar gets hidden but I see a white space at the bottom.
It is confirmed that the white space is due to Tabbar because when I changed the hidden property of tabBar to NO
(self.tabBarController.tabBar.hidden = No;)
I can see the tabBars at the place where the white space was coming earlier in landscape view.
I am sharing the code for shouldAutorotateToInterfaceOrientation, where I am creating the landscape view.
I have tried to use [UIScreen mainScreen].bounds.size.width instead of self.tabBarController.view.bounds.size.width.
Also I have tried hardCoding the value of width, but there is no change in the width of frame.
One more thing to make things clear, If anybody has doubt that why are we removing the landscape view from super view, making it nil and then redrawing it again.
This is required because our app shows weekdays - mon, tue, wed and app also supports changing to different language.
So when user selects a different language, we need to show these weekday labels in corresponding language so we have to remove the view and then redraw it again because UILabes do not get refreshed automatically.
I have read some posts on this issue but not able to trace out actual reason. Please if someone has any idea, do suggest a solution.
First , I am creating a landscape view in viewDidLoad() like this -
_landscapeView = [[MyWeekView alloc]initWithFrame:CGRectMake(0, 0, self.tabBarController.view.bounds.size.height, 320)];
[self.view addSubview:_landscapeView];
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
displayEmpName.frame = CGRectMake(0, 22.0, self.tabBarController.view.bounds.size.width, 34.0);
_portraitView.hidden = YES;
_landscapeView.hidden = NO;
self.tabBarController.tabBar.hidden = YES;
self.navigationController.navigationBar.hidden = YES;
[[self.tabBarController.view.subviews objectAtIndex:0]setFrame:CGRectMake(0, 0, 480,320)];
dayView.hidden = YES;
[self.view bringSubviewToFront:_landscapeView];
self.navigationItem.leftBarButtonItem = nil;
//LABELS IN SCREEN DO NOT CHANGE ON CHANGING LANGUAGE
//RELOAD THE LANDSCAPE VIEW ONCE DEVICE IS CHANGED TO LANDSCAPE MODE
[_landscapeView removeFromSuperview];
_landscapeView = nil;
_landscapeView = [[MyWeekView alloc]initWithFrame:CGRectMake(0, 0, self.tabBarController.view.bounds.size.width, 320)];
[self.view addSubview:_landscapeView];
}
I am writing an app that I would like to only be viewed in Landscape Mode. I have set it up so that if the iPhone is held in Portrait Mode, nothing happens and the current image remains in Landscape Mode. The iPhone Simulator starts out in Landscape Mode with the Home Button on the right. If the iPhone is rotated from one Landscape Mode to the other, animation then occurs and the view is adjusted. However, whenever the device is in Landscape Mode with the Home Button on the left, the image is 20 pixels higher than needed, revealing a white line at the bottom of the screen.
In spite of all attempts to logically adjust this such as
self.view.frame = CGRectMake (0,20, self.view.frame.size.width, self.view.frame.size.height)
it doesn't fix the problem. I am accounting for the Status Bar in my calculations.
The .xib file contains an UIImageView on top of a UIView. This is my first experience implementing these methods so I apologize if the solution is relatively easy. Below is the code for the two methods used to implement the Landscape Mode views.
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
//set up interface to only be viewed in Landscape
if(interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
return interfaceOrientation;
else if(interfaceOrientation == UIInterfaceOrientationLandscapeRight)
return interfaceOrientation;
else
return NO;
}
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)tointerfaceOrientation duration:(NSTimeInterval)duration {
if(UIInterfaceOrientationLandscapeRight){
self.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}
else if (UIInterfaceOrientationLandscapeLeft) {
//shouldn't adjustment to 20 fix the view?
self.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}
else return;
}
For UIViews to adjust the subviews properly you will have to take a look at this UIView property: autoresizingMask, and set the mask to autoresize what is needed, if that does not work you will have to override:
- (void)layoutSubviews
From the reference: UIView reference
This should be a pretty common thing to do, but I haven't been able to get it to work exactly right.
I have rectangular content. It normally fits in 320x361: portrait mode minus status bar minus ad minus tab bar.
I have put that content in a UIScrollView and enabled zooming. I also want interface rotation to work. The content will always be a tall rectangle, but when zoomed users might want to see more width at a time and less height.
What do I need to do in Interface Builder and code to get this done? How should I set my autoresizing on the different views? How do I set my contentSize and contentInsets?
I have tried a ton of different ways and nothing works exactly right. In various of my solutions, I've had problems with after some combination of zooming, interface rotation, and maybe scrolling, it's no longer possible to scroll to the entire content on the screen. Before you can see the edge of the content, the scroll view springs you back.
The way I'm doing it now is about 80% right. That is, out of 10 things it should do, it does 8 of them. The two things it does wrong are:
When zoomed in portrait mode, you can scroll past the edge of the content, and see a black background. That's not too much to complain about. At least you can see all the content. In landscape mode, zoomed or not, seeing the black background past the edge is normal, since the content doesn't have enough width to fill the screen at 1:1 zoom level (the minimum).
I am still getting content stuck off the edge when it runs on a test device running iOS 3.0, but it works on mine running 4.x. -- Actually that was with the previous solution. My tester hasn't tried the latest solution.
Here is the solution I'm currently using. To summarize, I have made the scroll view as wide and tall as it needs to be for either orientation, since I've found resizing it either manually or automatically adds complexity and is fragile.
View hierarchy:
view
scrollView
scrollableArea
content
ad
view is 320x411 and has all the autoresizing options on, so conforms to screen shape
scrollView is 480 x 361, starts at origin -80,0, and locks to top only and disables stretching
scrollableArea is 480 x 361 and locks to left and top. Since scrollView disables stretching, the autoresizing masks for its subviews don't matter, but I tell you anyway.
content is 320x361, starts at origin 80,0, and locks to top
I am setting scrollView.contentSize to 480x361.
shouldAutorotateToInterfaceOrientation supports all orientations except portrait upside down.
In didRotateFromInterfaceOrientation, I am setting a bottom content inset of 160 if the orientation is landscape, resetting to 0 if not. I am setting left and right indicator insets of 80 each if the orientation is portrait, resetting if not.
scrollView.minimumZoomScale = 1.0
scrollView.maximumZoomScale = 2.0
viewForZoomingInScrollView returns scrollableArea
// in IB it would be all options activated
scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
scrollView.contentSize = content.frame.size; // or bounds, try both
what do you mean with scrollableArea?
your minZoomScale is set to 1.0 thats fine for portrait mode but not for landscape. Because in landscape your height is smaller than in portrait you need to have a value smaller than 1.0. For me I use this implementation and call it every time, the frame of the scrollView did change:
- (void)setMaxMinZoomScalesForCurrentBounds {
CGSize boundsSize = self.bounds.size; // self is a UIScrollView here
CGSize contentSize = content.bounds.size;
CGFloat xScale = boundsSize.width / contentSize.width;
CGFloat yScale = boundsSize.height / contentSize.height;
CGFloat minScale = MIN(xScale, yScale);
if (self.zoomScale < minScale) {
[self setZoomScale:minScale animated:NO];
}
if (minScale<self.maximumZoomScale) self.minimumZoomScale = minScale;
//[self setZoomScale:minScale animated:YES];
}
- (void)setFrame:(CGRect)rect { // again, this class is a UIScrollView
[super setFrame:rect];
[self setMaxMinZoomScalesForCurrentBounds];
}
I don't think I understood the entire problem from your post, but here's an answer for what I did understand.
As far as I know (and worked with UIScrollView), the content inside a UIScrollView is not automatically autoresized along with the UIScrollView.
Consider the UIScrollView as a window/portal to another universe where your content is. When autoresizing the UIScrollView, you are only changing the shape/size of the viewing window... not the size of the content in the other universe.
However, if needed you can intercept the rotation event and manually change your content too (with animation so that it looks good).
For a correct autoresize, you should change the contentSize for the scrollView (so that it knows the size of your universe) but also change the size of UIView. I think this is why you were able to scroll and get that black content. Maybe you just updated the contentSize, but now the actuall content views.
Personally, I haven't encountered any case that required to resize the content along with the UIScrollView, but I hope this will get you started in the right direction.
If I understand correctly is that you want a scrollview with an image on it. It needs to be fullscreen to start with and you need to be able to zoom in. On top of that you want it to be able to rotate according to orientation.
Well I've been prototyping with this in the past and if all of the above is correct the following code should work for you.
I left a bit of a white area for the bars/custombars.
- (void)viewDidLoad
{
[super viewDidLoad];
//first inits and allocs
scrollView2 = [[UIScrollView alloc] initWithFrame:self.view.frame];
imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"someImageName"]];
[scrollView2 addSubview:imageView];
[self drawContent]; //refreshing the content
[self.view addSubview:scrollView2];
}
-(void)drawContent
{
//this refreshes the screen to the right sizes and zoomscales.
[scrollView2 setBackgroundColor:[UIColor blackColor]];
[scrollView2 setCanCancelContentTouches:NO];
scrollView2.clipsToBounds = YES;
[scrollView2 setDelegate:self];
scrollView2.indicatorStyle = UIScrollViewIndicatorStyleWhite;
[scrollView2 setContentSize:CGSizeMake(imageView.frame.size.width, imageView.frame.size.height)];
[scrollView2 setScrollEnabled:YES];
float minZoomScale;
float zoomHeight = imageView.frame.size.height / scrollView2.frame.size.height;
float zoomWidth = imageView.frame.size.width / scrollView2.frame.size.width;
if(zoomWidth > zoomHeight)
{
minZoomScale = 1.0 / zoomWidth;
}
else
{
minZoomScale = 1.0 / zoomHeight;
}
[scrollView2 setMinimumZoomScale:minZoomScale];
[scrollView2 setMaximumZoomScale:7.5];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
if (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
// Portrait
//the 88pxls is the white area that is left for the navbar etc.
self.scrollView2.frame = CGRectMake(0, 88, [UIScreen mainScreen].bounds.size.width, self.view.frame.size.height - 88);
[self drawContent];
}
else {
// Landscape
//the 88pxls is the white area that is left for the navbar etc.
self.scrollView2.frame = CGRectMake(0, 88, [UIScreen mainScreen].bounds.size.height, self.view.frame.size.width);
[self drawContent];
}
return YES;
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
I hope this will fix your troubles. If not leave a comment.
When you want to put a content (a UIView instance, let's call it theViewInstance ) in a UIScrollView and then scroll / zoom on theViewInstance , the way to do it is :
theViewInstance should be added as the subview of the UIScrollView
set a delegate to the UIScrollView instance and implement the selector to return the view that should be used for zooming / scrolling:
-(UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return theViewInstance;
}
Set the contentSize of the UIScrollView to the frame of the theViewInstance by default:
scrollView.contentSize=theViewInstance.frame.size;
(Additionally, the accepted zoom levels can be set in the UIScrollView :)
scrollView.minimumZoomScale=1.0;
scrollView.maximumZoomScale=3.0;
This is the way a pinch to zoom is achieved on a UIImage : a UIImageView is added to a UIScrollView and in the UIScrollViewDelegate implementation, the UIImageView is returned (as described here for instance).
For the rotation support, this is done in the UIViewController whose UIView contains the UIScrollView we just talked about.
Why doesn't this UIView layout code work as I want?
Background:
I have a custom UIView I have in my UIViewController
The custom UIView has a clock background imageview and an hourhand imageview
After introducing some code (see below) to try to resize the hour hand for an orientation change I'm getting stuck.
The code below really stuffs things up - the border outlike of the hour glass hand is way off with this code
Any ideas what I'm doing wrong - there's obvious some assumption or misunderstanding I have for it to screw up like this...
Code called by UIViewController (in "didRotateFromInterfaceOrientation" method) to my custom view:
// Centre image
self.hourHandImageView.center = self.hourFaceUIImageView.center;
// Resize to cater for either Portrait or Landscape orientation
CGSize currSize = self.hourFaceUIImageView.frame.size;
float newHourHandImageheightWidth = currSize.width < currSize.height ? currSize.width : currSize.height;
CGSize newHourHandViewSize = CGSizeMake(newHourHandImageheightWidth, newHourHandImageheightWidth);
CGRect newRect = self.hourHandImageView.frame;
newRect.size = newHourHandViewSize;
self.hourHandImageView.frame = newRect;
PS. Without the "Resize to cater for either Portrait or Landscape orientation" code the hour hand correctly stays centered where it should (noting I use transformation to turn it around). There all I really need to do is resize the hour hand appropriately after an orientation change, noting the hour background image is smaller in the landscape mode.
First, how can you tell if you are in portrait or landscape orientation? With this (?):
float newHourHandImageheightWidth = currSize.width < currSize.height ? currSize.width : currSize.height;
What i would advice you, is to do something like this:
-
(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
[self reOrderToPortrait];
} else if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)){
[self reOrderToLandScape];
}
}
Then on each method, you should define the new frames for your views. Start by removing your autosizing from your views, because you will define the new frames, and you dont want that to interfere with. You could also define some mesures like this:
#define hourHandImageViewPortrait CGPointMake(...,...)
#define hourHandImageViewLandScape CGPointMake(..., ...)
I'm trying to create an iPhone application that is always in landscape mode, using the Utility application template. Here's what I did:
Create a new iPhone application project, using the Utility Application template
In Interface Builder, rotate all the views 90 degrees.
In Interface Builder, add a label to the middle of the MainView. Stretch it all the way across the view, set the alignment to centered, and set the autosizing springs so that it can stretch horizontally.
In Info.plist, add the key "UIInterfaceOrientation" with value "UIInterfaceOrientationLandscapeRight"
In the controller classes, change the shouldAutorotateToInterfaceOrientation methods to "return (interfaceOrientation == UIInterfaceOrientationLandscapeRight) || (interfaceOrientation == UIInterfaceOrientationLandscapeLeft);"
Run the app.
When I launch my app, it comes up in landscape orientation, but the main view only covers the top half of the display, and it is stretched horizontally. I get the same results in both the simulator and on an actual device. I've seen it with versions 2.2 and 2.2.1 of the SDK.
I have been able to work around the problem by adding the following step to the above:
Add "self.view.autoresizesSubviews = NO;" to RootViewController's viewDidLoad method after "[super viewDidLoad];".
If I do this, then it works as expected. But this feels like a hack. Why should this be necessary?
I don't think it is a transformation issue. All elements are drawn in the proper orientation and with the proper scaling. The problem seems to be that the bounds rectangles of the main view gets funky. It looks like the height of the main view is being cut by a little more than half, and the width is being increased by about 50%.
If I do the exact same set of steps using the View-based Application template instead of Utility, then everything works as expected. So I'm pretty sure the problem is specific to how a Utility application manages its views.
Anybody understand what's going on here?
I was going to say that setting this key does not rotate your interface; you still need to lay out your content in landscape mode and do the appropriate rotation using CFAffineTransform - see "Launching in Landscape Mode" in iPhone OS Programming Guide. Going to find the reference for you, I found this comment: "To launch a view controller–based application in landscape mode in versions of iPhone OS prior to v2.1, you need to apply a 90 degree rotation to the transform of the application’s root view in addition to all the preceding steps. Prior to iPhone OS 2.1, view controllers did not automatically rotate their views based on the value of the UIInterfaceOrientation key. This step is not necessary in iPhone OS 2.1 and later, however."
So if you're running pre-2.1, you need to add this code to your viewDidLoad method in your view controller. (Otherwise, can you post some code?)
-(void)viewDidLoad
// After loading the view, transform the view so that the co-ordinates are right for landscape
// As described in iPhone Application Programming Guide
// Weird, I'm sure this used to be needed, but it doesn't now. The one in CardScrollViewController is needed though.
{
[super viewDidLoad];
CGAffineTransform transform = self.view.transform;
CGPoint center = CGPointMake(kScreenHeight / 2.0, kScreenWidth / 2.0);
// Set the center point of the view to the center point of the window's content area.
self.view.center = center;
// Rotate the view 90 degrees around its new center point.
transform = CGAffineTransformRotate(transform, (M_PI / 2.0));
self.view.transform = transform;
}
Jane describes the setting of UIInterfaceOrientation to UIInterfaceOrientationLandscapeRight (or UIInterfaceOrientationLandscapeLeft), and the rotation settings recommended in the documentation, but I used a slightly different block of code (to the same end) in my root view controller:
- (void)loadView
{
UIView *primaryView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
primaryView.backgroundColor = [UIColor clearColor];
// Start in landscape orientation, and stay that way
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationLandscapeRight)
{
CGAffineTransform transform = primaryView.transform;
// Use the status bar frame to determine the center point of the window's content area.
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
CGRect bounds = CGRectMake(0, 0, statusBarFrame.size.height, statusBarFrame.origin.x);
CGPoint center = CGPointMake(60.0, bounds.size.height / 2.0);
// Set the center point of the view to the center point of the window's content area.
primaryView.center = center;
// Rotate the view 90 degrees around its new center point.
transform = CGAffineTransformRotate(transform, (M_PI / 2.0));
primaryView.transform = transform;
}
self.view = primaryView;
[primaryView release];
}
In addition to that, I implemented the following delegate method in my root view controller:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return ( (interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (interfaceOrientation == UIInterfaceOrientationLandscapeRight));
}
Finally, I was encountering weird glitches with the Simulator not auto-rotating properly, so I needed to implement the following delegate method in my UIApplicationDelegate:
- (void)application:(UIApplication *)application willChangeStatusBarOrientation:(UIInterfaceOrientation)newStatusBarOrientation duration:(NSTimeInterval)duration;
{
// This prevents the view from autorotating to portrait in the simulator
if ((newStatusBarOrientation == UIInterfaceOrientationPortrait) || (newStatusBarOrientation == UIInterfaceOrientationPortraitUpsideDown))
[application setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO];
}
After all that, my application was able to start in landscape (right) and stay in that orientation under the 2.0 firmware and in the Simulator.
Try setting the orientation property of the view to Landscape in the nib. This property can be found in 4th tab[Attributes Inspector] of Info View of the UIView under Simulated Metrices.