I'm using setting the background image using methodology below. When I rotate my device the background repeats, which make sense because it is not an image. How do I deal with orientation change if this is the way I'm setting my background image?
- (void)viewDidLoad {
[super viewDidLoad];
UIColor *background = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background.png"]];
self.view.backgroundColor = background;
[background release];
}
It took me awhile to understand this concept. I didn't want to create the same image portrait and landscape. The key here is that CGAffineTransformMakeRotation rotates from the original state of your UIImageView or any UIView for that matter. This assumes your background image has orientation to it. E.g. You want your UIImageView to stay put, while other objects behaves to normal orientation change event.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
backgroundImage.transform = CGAffineTransformMakeRotation(M_PI / 2);
}
else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight){
backgroundImage.transform = CGAffineTransformMakeRotation(-M_PI / 2);
}
else {
backgroundImage.transform = CGAffineTransformMakeRotation(0.0);
}
}
- (void)viewDidLoad {
[super viewDidLoad];
//set the UIImageView to current UIView frame
backgroundImage.frame = self.view.frame;
}
You have to take 2 images both for horizontal and vertical and instead of allocating you can use [...: colorWithPatternImage:...]; and set it when orientation is changed to the background of the view.
hAPPY iCODING...
If I understand correctly, Your background gets created or overwritten every time you change the orientation right. By default backgroundColor is nil. You can check for this, if it is nil then you go ahead and set the values.
Its like
if ( self.view.backgroundColor == nil){
//set the new values here
}
Related
i am doing application where i have taken two views Portrait and Landscape. i am creating a label programitically and calling it by parameter passing.As i want the position of the label to be changed in the landscapeView so i am taking two views.Instead i want to do in a SingleView
instead of using two views i want to have only one view and set the label position accordingly in portrait and landscape.
so please suggest me how to have only one view and change the textLabel position according to the view..please suggest me with sample code.
At present i am using the below code
[self.portraitView addSubview:[self createLabel:CGRectMake(450,140,60,20):#"Ratings:"]];
[self.landscapeView addSubview:[self createLabel:CGRectMake(600,140,60,20):#"Ratings:"]];
-(UILabel*)createLabel:(CGRect)frame :(NSString*)labelTitle {
UILabel *myLabel = [[UILabel alloc] initWithFrame:frame];
[UIFont fontWithName:#"Arial" size:13];
myLabel.text = labelTitle;
}
Make your UILabel *myLabel a class variable by declaring it in your .h file.
Call the createLabel method in the viewDidLoad method of your viewController based on the correct device orientation.
-(void) viewDidLoad
{
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation
if(orientation == UIDeviceOrientationPortrait)
CGRect frame = CGRectMake(450,140,60,20);
else
CGRect frame = CGRectMake(600,140,60,20);
myLabel = [[UILabel alloc] initWithFrame:frame];
[UIFont fontWithName:#"Arial" size:13];
myLabel.text = labelTitle;
}
Reset the frame in this method
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if(interfaceOrientation == UIInterfaceOrientationLandScape)
{
mylabel.frame = CGRectMake(600,140,60,20); //your landscape frame
mylabel.text = #"Ratings:";
}
else
{
mylabel.frame = CGRectMake(450,140,60,20); // your portrait frame
mylabel.text = #"Ratings:";
}
}
You will need to dig around a little bit for covering all the possible orientations in this method.
Cheers!!!
in your code there is a method called
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if(interfaceOrientation == UIInterfaceOrientationLandScape)
//set label frame here
}
Implement the following two methods in your viewcontroller.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
and
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
In willRotateToInterfaceOrientation:duration: method, set the required frame to the label instance.
You should set the labels frame in the layoutSubviews method of the UILabel's superview and check the superviews bounds or the interface orientation to determine the appropriate frame for the label. If you also use autoresizingMask on the UILabel correctly, the transition will be animated nicely.
Also: why do you use two views? instead of applying different layouts to one?
The implementation of pinch zoom is been done by using UIScrollView and putting 'UIImageView` inside it, through following code:
-(void)viewDidLoad{
slideShowImageViewScrollView.maximumZoomScale = 4.0;
slideShowImageViewScrollView.minimumZoomScale = 1.0;
slideShowImageViewScrollView.clipsToBounds = YES;
slideShowImageViewScrollView.delegate = self;
slideShowImageViewScrollView.scrollEnabled = NO;
}
Then after checking zoomScale scrolling is been enabled as:
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
if (scrollView.zoomScale!=1.0)
{
slideShowImageViewScrollView.scrollEnabled = YES;
}
else
{
slideShowImageViewScrollView.scrollEnabled = NO;
}
}
And here is the view that is for zooming in, in scroll view
- (UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return slideShowImageView;
}
Now using timer the image of UIImageView is been changed after a time period, by following code:
- (void) changeImageSlide
{
if (imageCounter>=totalNoOfImages-1)
{
imageCounter=0;
}
NSString *str = [NSString stringWithFormat:#"%#.jpg",[copyOf_myGlobleArrayOfImageIds objectAtIndex:imageCounter]];
mainSlideShowImageView setImage:[UIImage imageNamed:str]];
[mainSlideShowImageView setTag:[[copyOf_myGlobleArrayOfImageIds objectAtIndex:imageCounter] intValue]];
imageCounter++;
}
Only thing problamatic here is when the next image is loaded that is also, 'zoomed in', we want image in normal mode.
Can we achieve it(by coding it for zoom out in our changeImageSlide method or any where)?
If yes, then how?
If no, then is there any alternative?
Thank You.
As you load the new image, set the zoomScale of the scrollView to 1.0.
[slideShowImageViewScrollView setZoomScale:1.0 animated:NO];
You can set the zoom scale on the scrollview to reset it to 1.0. You could even store zoom levels against images so that it preserves the individual zoom setting per image.
I have a very simply UIViewController, and I'm trying to figure out how to use willRotateToInterfaceOrientation. my UIViewController has a very simple viewDidLoad method:
-(void)viewDidLoad {
[super viewDidLoad];
theBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 48.0f)];
theBar.tintColor = [UIColor orangeColor];
UINavigationItem *item = [[UINavigationItem alloc] initWithTitle:#"The Title"];
item.hidesBackButton = YES;
[theBar pushNavigationItem:item animated:YES];
[item release];
[self.view addSubview:theBar];
}
So basically, I just have a UINavigationBar at the top of my controller. That's it. I implemented some methods for rotation, based on what I found online:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
-(void)willRotateToInterfaceOrientation: (UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration {
if ((orientation == UIInterfaceOrientationLandscapeLeft) || (orientation == UIInterfaceOrientationLandscapeRight)) {
theBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, 640, 48)];
}
}
So, I launch the app in portrait mode, and then I twist in in landscape mode. And basically, theBar still stays it's normal size, and doesn't get resized. I'm sure this is a silly question, but what is the proper way to use the rotation capability? I want to make it so that it also works if the app is launched in landscape mode. What is the best way to initialize my components when the UIViewController first launches, keeping in mind that I want support for both orientations, and also keeping in mind that I want to be able to change the size of everything based on orientation changes throughout the duration of the life of the UIViewController? Thanks!
What you want to do is change the frame of your existing theBar object, and not instantiate a new one. You can do that with something like this:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation
duration:(NSTimeInterval)duration {
CGRect f = CGRectMake(0,
0,
CGRectGetWidth(self.view.frame),
CGRectGetHeight(theBar.frame);
theBar.frame = f;
}
Note that the value of self.view.frame is used, which contains values post rotation. Also note that the function I'm using here is different than yours. I haven't tested it with the function you're using, so I can't say if that'll work or not. Finally, you can avoid this altogether by just setting the autoresizingmask on theBar in viewDidLoad instead:
[theBar setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin];
I'm writing an iPhone app that (like most apps) supports auto-rotation: You rotate your phone, and its views rotate and resize appropriately.
But I am assigning a custom view to navigationItem.titleView (the title area of the navigation bar), and I can't get that view to resize correctly when the phone rotates.
I know what you're thinking, "Just set its autoresizingMask to UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight," but it's not that simple. Of course, if I don't set my view's autoresizingMask, then my view doesn't resize; and I want it to resize.
The problem is, if I do set its autoresizingMask, then it resizes correctly as long as that view is visible; but the titleView's size gets messed up in this scenario:
Run the app, with the phone held in portrait mode. Everything looks good.
Do something that causes the app to push another view onto the navigation stack. E.g. click a table row or button that causes a call to [self.navigationController pushViewController:someOtherViewController animated:YES].
While viewing the child controller, rotate the phone to landscape.
Click the "Back" button to return to the top-level view. At this point, the title view is messed up: Although you are holding the phone in landscape mode, the title view is still sized as if you were holding it in portrait mode.
Finally, rotate the phone back to portrait mode. Now things get even worse: The title view shrinks in size (since the navigation bar got smaller), but since it was already too small, now it is much too small.
If you want to reproduce this yourself, follow these steps (this is a bit of work):
Make an app using Xcode's "Navigation-based Application" wizard.
Set it up so that the top-level table view has rows that, when you click them, push a detail view onto the navigation stack.
Include this code in both the top-level view controller and the detail view controller:
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
Include this code in only the top-level view controller:
- (void)viewDidLoad {
[super viewDidLoad];
// Create "Back" button
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Master"
style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];
// Create title view
UILabel* titleView = [[[UILabel alloc] initWithFrame:CGRectMake(0,0,500,38)] autorelease];
titleView.textAlignment = UITextAlignmentCenter;
titleView.text = #"Watch this title view";
// If I leave the following line turned on, then resizing of the title view
// messes up if I:
//
// 1. Start at the master view (which uses this title view) in portrait
// 2. Navigate to the detail view
// 3. Rotate the phone to landscape
// 4. Navigate back to the master view
// 5. Rotate the phone back to portrait
//
// On the other hand, if I remove the following line, then I get a different
// problem: The title view doesn't resize as I want it to when I:
//
// 1. Start at the master view (which uses this title view) in portrait
// 2. Rotate the phone to landscape
titleView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.navigationItem.titleView = titleView;
}
Finally, follow my repro steps.
So ... am I doing something wrong? Is there a way to make my titleView always resize correctly?
You should also set the contentMode of the UIImageView to get the titleView properly displayed in landscape and/or portrait mode :
imgView.contentMode=UIViewContentModeScaleAspectFit;
The whole sequence: (self is a UIViewController instance)
UIImageView* imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"myCustomTitle.png"]];
imgView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imgView.contentMode=UIViewContentModeScaleAspectFit;
self.navigationItem.titleView = imgView;
[imgView release];
I had something similar - but it was returning (popping) to root view controller. Ultimately, I went with the following for popping:
[[self navigationController] setNavigationBarHidden:YES animated:NO];
[[self navigationController] popViewControllerAnimated:YES];
[[self navigationController] setNavigationBarHidden:NO animated:NO];
And it worked. There may have been a better way but - after all the hours I'd already spent on this issue - this was good enough for me.
I dealt with this same issue by keeping track of the customView's initial frame, then toggling between that and a scaled CGRect of the initial frame in a -setLandscape method on a UIButton subclass. I used the UIButton subclass as navigationItem.titleView and navigationItem.rightBarButtonItem.
In UIButton subclass -
- (void)setLandscape:(BOOL)value
{
isLandscape = value;
CGFloat navbarPortraitHeight = 44;
CGFloat navbarLandscapeHeight = 32;
CGRect initialFrame = // your initial frame
CGFloat scaleFactor = floorf((navbarLandscapeHeight/navbarPortraitHeight) * 100) / 100;
if (isLandscape) {
self.frame = CGRectApplyAffineTransform(initialFrame, CGAffineTransformMakeScale(scaleFactor, scaleFactor));
} else {
self.frame = initialFrame;
}
}
Then in the InterfaceOrientation delegates I invoked the -setLandscape method on the customViews to change their sizes.
In UIViewController -
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[self updateNavbarButtonsToDeviceOrientation];;
}
- (void)updateNavbarButtonsToDeviceOrientation
{
ResizeButton *rightButton = (ResizeButton *)self.navigationItem.rightBarButtonItem.customView;
ResizeButton *titleView = (ResizeButton *)self.navigationItem.titleView;
if (self.interfaceOrientation == UIDeviceOrientationPortrait || self.interfaceOrientation == UIDeviceOrientationPortraitUpsideDown) {
[rightButton setLandscape:NO];
[titleView setLandscape:NO];
} else {
[rightButton setLandscape:YES];
[titleView setLandscape:YES];
}
}
(Answering my own question)
I got this working by manually keeping track of the titleView's margins (its distance from the edges of the navigtion bar) -- saving when the view disappears, and restoring when the view reappears.
The idea is, we aren't restoring the titleView to the exact size it had previously; rather, we are restoring it so that it has the same margins it had previously. That way, if the phone has rotated, the titleView will have a new, appropriate size.
Here is my code:
In my view controller's .h file:
#interface MyViewController ...
{
CGRect titleSuperviewBounds;
UIEdgeInsets titleViewMargins;
}
In my view controller's .m file:
/**
* Helper function: Given a parent view's bounds and a child view's frame,
* calculate the margins of the child view.
*/
- (UIEdgeInsets) calcMarginsFromParentBounds:(CGRect)parentBounds
childFrame:(CGRect)childFrame {
UIEdgeInsets margins;
margins.left = childFrame.origin.x;
margins.top = childFrame.origin.y;
margins.right = parentBounds.size.width -
(childFrame.origin.x + childFrame.size.width);
margins.bottom = parentBounds.size.height -
(childFrame.origin.y + childFrame.size.height);
return margins;
}
- (void)viewDidUnload {
[super viewDidUnload];
titleSuperviewBounds = CGRectZero;
titleViewMargins = UIEdgeInsetsZero;
}
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Keep track of bounds information, so that if the user changes the
// phone's orientation while we are in a different view, then when we
// return to this view, we can fix the titleView's size.
titleSuperviewBounds = self.navigationItem.titleView.superview.bounds;
CGRect titleViewFrame = self.navigationItem.titleView.frame;
titleViewMargins = [self calcMarginsFromParentBounds:titleSuperviewBounds
childFrame:titleViewFrame];
}
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Check for the case where the user went into a different view, then
// changed the phone's orientation, then returned to this view. In that
// case, our titleView probably has the wrong size, and we need to fix it.
if (titleSuperviewBounds.size.width > 0) {
CGRect newSuperviewBounds =
self.navigationItem.titleView.superview.bounds;
if (newSuperviewBounds.size.width > 0 &&
!CGRectEqualToRect(titleSuperviewBounds, newSuperviewBounds))
{
CGRect newFrame = UIEdgeInsetsInsetRect(newSuperviewBounds,
titleViewMargins);
newFrame.size.height =
self.navigationItem.titleView.frame.size.height;
newFrame.origin.y = floor((newSuperviewBounds.size.height -
self.navigationItem.titleView.frame.size.height) / 2);
self.navigationItem.titleView.frame = newFrame;
}
}
}
For IOS5 onwards, as this is an old question...This is how I accomplished the same issue with the title text not aligning properly.
[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:2 forBarMetrics:UIBarMetricsLandscapePhone];
Tested on ios5/6 sims works fine.
This is what I did:
self.viewTitle.frame = self.navigationController.navigationBar.frame;
self.navigationItem.titleView = self.viewTitle;
The viewTitle is a view created in the xib, it takes the size of the navigationBar and after it has been added the titleView adjust the size to leave room to the back button. Rotations seem to work fine.
I had had same problem, but I seem to get workaround with following code.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UIView *urlField = self.navigationItem.leftBarButtonItem.customView;
CGRect frame = urlField.frame;
frame.size.width = 1000;
urlField.frame = frame;
}
In my case, the custom view is a UITextField, but I hope this will help you.
I'm developing an app for iPad and I try to handle multiple orientation.
My app contains a webview and a loading UIImageView that appears when my webview is loading content.
This UIImageView has a background image that I set in InterfaceBuilder. When I change orientation to landscape, the image is cut.
I'd like the UIImageView to set image-portrait.png when the ipad is in portrait mode and image-landscape.png when it's in landscape mode.
Thank you for your help and advices!
Screenshots :
I found a solution :
In Interface Builder, I set the autosizing of my ImageView to auto fill the screen.
In my ViewController, I add a method to detect the change of orientation and I set the appropriate image depending if the iPad is in portrait or landscape :
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if((self.interfaceOrientation == UIDeviceOrientationLandscapeLeft) || (self.interfaceOrientation == UIDeviceOrientationLandscapeRight)){
myImageView.image = [UIImage imageNamed:#"image-landscape.png"];
} else if((self.interfaceOrientation == UIDeviceOrientationPortrait) || (self.interfaceOrientation == UIDeviceOrientationPortraitUpsideDown)){
myImageView.image = [UIImage imageNamed:#"image-portrait.png"];
} }
It took me awhile to understand this concept. I didn't want to create the same image portrait and landscape. The key here is that CGAffineTransformMakeRotation rotates from the original state of your UIImageView or any UIView for that matter. This assumes your background image has orientation to it. E.g. You want your UIImageView to stay put, while other objects behaves to normal orientation change event.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
backgroundImage.transform = CGAffineTransformMakeRotation(M_PI / 2);
}
else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight){
backgroundImage.transform = CGAffineTransformMakeRotation(-M_PI / 2);
}
else {
backgroundImage.transform = CGAffineTransformMakeRotation(0.0);
}
}
You can handle the orientation by autoresizing the view.
UIImageView *imageView=[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"background-image"]];
imageView.frame = self.view.bounds;
imageView.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight
iimageView.contentMode = UIViewContentModeScaleAspectFill;
[self.view addSubview:imageView];
[self.view sendSubviewToBack:imageView];
[imageView release];
This will be make solution to your problem.
While Salah your answer looks ok to me i believe you can do two improvements here:
Set the background image within this function:
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
duration (NSTimeInterval)duration
if you do the change within the didRotateFromInterfaceOrientation
you will change the background image once you have finished to
rotate the IPad and the transition from the two background image
won't be smooth: you will clearly see the new background image popup
at the end of the rotation.
Improve setting the myImageView.image value:
_myImageView.image = UIInterfaceOrientationIsLandscape(interfaceOrientation) ? [UIImage
imageNamed:#"image-landscape.png"] : [UIImage
imageNamed:#"image-portrait.png"];
I'd actually add another branch to docchang's code as when the iPad is rotated to portrait upside own it uses the portrait right-side-up image which can look a little odd.
I added,
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft)
{
imageBackgroundView.transform = CGAffineTransformMakeRotation(M_PI / 2);
}
else
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
imageBackgroundView.transform = CGAffineTransformMakeRotation(-M_PI / 2);
}
else
if (toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
imageBackgroundView.transform = CGAffineTransformMakeRotation(M_PI);
}
else
{
imageBackgroundView.transform = CGAffineTransformMakeRotation(0);
}
It is interesting that programmers have such a tough time thinking outside the box (literally in this case).
Try this (how I solved this myself).
Create a square image.
Set the constraints of the uiimageview to fill in the screen (leading, trailing, top, bottom)
Set the mode to aspect fill (which will enlarge the image, but keep its aspect ratio constant)
Done.
The key here is, of course, that you should create a square image (which is, as I said above, outside the box ;-)