I've got an UIImageView as a background image for my application, which, like a helicopter in distress, autorotates. The problem is when the orientation autorotates my image gets shifted all over the place and I don't like it. Since it's a simple texture I figure the user would prefer to have it stay in its place, and not autorotate. What I mean by this is that I would like the image to stay full screen, and when the user rotates the phone the image is just rotated 90 degrees so it appears the image is static with the phone, not the rest of the application.
You can try implementing the willAnimateRotationToInterfaceOrientation:duration: method in your view controller and rotating the UIImageView in the opposite direction.
The code below is from the top of my head, I can't guarantee that it will work:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
CGFloat angle = M_PI/2; // Figure out the proper angle
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
imageview.transform = CGAffineTransformMakeRotation(angle);
[UIView commitAnimations];
}
I suggest to rotate your image in an image-editing-software and load it dynamically. This is far, far easier to do, than messing around with the angles (and also the code is easier to read).
Like this:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (toInterfaceOrientation == UIInterfaceOrientationPortrait || toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
myBackground.image = [UIImage imageNamed:#"bg.jpg"];
} else { // landscape view
myBackground.image = [UIImage imageNamed:#"bg_landscape.jpg"];
}
}
Related
My tableview has 4 cells, and each cells has one different picture as background.
When I rotate the iPhone, the height of the rows change, and also the pictures.
Is really easy to change picture if the position is in landscape or in portrait, but the picture change when the iPhone is already rotated, not while is rotating!
What's the problem?
For example: the iPhone is in portrait and the user rotates it in landscape.
That there is a very ugly effect, because the pictures change just when the iPhone is in landscape, and for a fraction of a second the user sees the portrait picture deformed (before that it changes).
How can I solve it?
Thanks
The easy way:
Implement willAnimateRotationToInterfaceOrientation in your view controller. Inside this method you can reorient your pictures and they'll automatically be animated. There's a short overview this and other available interface orientation methods at http://www.dizzey.com/development/ios/handling-layout-on-uiinterfaceorientation-change/
Pointlessly difficult way:
You could manually track acceleration and write your own animation blocks.
Set your view controller as a UIAccelerometerDelegate. Reference the sharedAccelerometer as follows.
accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
accel.updateInterval = 1.0f/60.0f;
Then implement this delegate method. This will be run every time the accelerometer notices acceleration. Tracking acceleration in the X axis will give you landscape/portrait orientation.
#pragma mark UIAccelerometer delegate method
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
if(acceleration.x > 0.8) //landscape right
{
[UIView beginAnimations:#"text spin" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDuration:0.2];
//do some animation to landscape right
[UIView commitAnimations];
}
if(acceleration.x < -0.8) //landscape left
{
[UIView beginAnimations:#"text spin" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDuration:0.2];
//do some animation to landscape left
[UIView commitAnimations];
}
if(acceleration.x < 0.2 && acceleration.x > -0.2) //portrait
{
[UIView beginAnimations:#"text spin" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDuration:0.2];
//do some animation to portrait view
[UIView commitAnimations];
}
In a situation like this, during a rotation I tend to just use the .hidden property on elements (myObject.hidden = YES) and then restore them when the rotation is complete.
I imagine your picture frames have more room in landscape view - another suggestion if this is true, is to keep them the same size in both orientations, then you wouldn't need to hide them during a rotation, for example.
Hope this helps!
Ended up with doing this:
Easiest way to support multiple orientations? How do I load a custom NIB when the application is in Landscape? Works superb!
I have made an image in Photoshop which I want to use as background for my info-screen in my iPad application. The image contains text and some icons also. Around the image I have a border which is green.
The effect I am trying to achieve is:
When the user goes from the portrait orientation to landscape orientation I want the image (just the frame and the icons) to rotate 90 degrees so the image appear in landscape mode, instead of having a portrait view of the frame in landscape. The text and icons are decoupled (different layers which I have organized in different UIImageView's)they shall rotate 90 degrees.
What I have done already is the following:
Experimented a bit with this method:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:
and tried to do this:
self.btnFacebook.transform = CGAffineTransformMakeRotation(1.5707964);
which I think will rotate the btnFacebook property 90 degrees to the right (correct me if I am wrong) since it specifies positive radians. I can't seem to get it to work correctly though. Should'nt this rotate the button 90 degrees around its center coordinate in the frame? That wouldn't cause a change in the position of the button (the button is square)?
EDIT
Made an image:
As the image shows the image background (some custom graphics on it which looks good in both orientations) goes from portrait to landscape and rotates so it does not appear as portrait in landscape, the icons are decoupled from the background so they need to rotate as well because they need to be in the right orientation (it is social icons). The text however are on the same position, it only rotates 90 degrees without repositioning.
I understand your question as that you are trying to not to rotate the interface as whole but to rotate the purple and red squares individually.
I created a UIViewController that resembles your layout.
The black square is a UIView and the white square is there only so that I can tell when the black view rotates. This view is wired to view1 property on the controller.
There are 4 buttons that are wired to btnx (x runs 1 through 4) properties.
Since I no longer want to auto rotate the interface I only support the portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
To do the rotation manually, I added a method to the ViewController. It determines the angle it needs to rotate the components from portrait to current orientation, creates a rotation transform and applies it to all outlets.
- (void)deviceDidRotate:(NSNotification *)notification
{
UIDeviceOrientation currentOrientation = [[UIDevice currentDevice] orientation];
double rotation = 0;
UIInterfaceOrientation statusBarOrientation;
switch (currentOrientation) {
case UIDeviceOrientationFaceDown:
case UIDeviceOrientationFaceUp:
case UIDeviceOrientationUnknown:
return;
case UIDeviceOrientationPortrait:
rotation = 0;
statusBarOrientation = UIInterfaceOrientationPortrait;
break;
case UIDeviceOrientationPortraitUpsideDown:
rotation = -M_PI;
statusBarOrientation = UIInterfaceOrientationPortraitUpsideDown;
break;
case UIDeviceOrientationLandscapeLeft:
rotation = M_PI_2;
statusBarOrientation = UIInterfaceOrientationLandscapeRight;
break;
case UIDeviceOrientationLandscapeRight:
rotation = -M_PI_2;
statusBarOrientation = UIInterfaceOrientationLandscapeLeft;
break;
}
CGAffineTransform transform = CGAffineTransformMakeRotation(rotation);
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
[self.btn1 setTransform:transform];
[self.btn2 setTransform:transform];
[self.btn3 setTransform:transform];
[self.btn4 setTransform:transform];
[self.view1 setTransform:transform];
[[UIApplication sharedApplication] setStatusBarOrientation:statusBarOrientation];
} completion:nil];
}
The last thing to do is to get the OS to call my method. To achieve that I added the following code to application:didFinishLaunchingWithOptions: in the AppDelegate.
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self.viewController selector:#selector(deviceDidRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
I am not sure whether this is exactly what you wanted but I believe it is at least similar so you can get some ideas from it how to solve your problem. I can provide source code for a working iPad application that I created to illustrate this.
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 ;-)
I need to rotate and move objects on a UIView when the orientation changes. To that end I have the following in willRotateToInterfaceOrientation
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
[self repositionObjectAfterRotation:scrollView x:0 y:100 width:480 height:150];
[self repositionObjectAfterRotation:pageControl x:50 y:430 width:100 height:30];
}
My repositionObjectAfterRotation:x:y:width:height: looks like this, which takes the object and alters its bounds, before setting the new bounds.
- (void)repositionObjectAfterRotation:(UIView *)anObject x:(int)x y:(int)y
width:(int)width height:(int)height
{
[UIView animateWithDuration:kAnimationDuration animations:^{
CGRect boundsRect = anObject.bounds;
boundsRect.origin.x = x;
boundsRect.origin.y = y;
boundsRect.size.width = width;
boundsRect.size.height = height;
anObject.bounds = boundsRect;
} completion:^ (BOOL finished){
if (finished) {
[UIView animateWithDuration:kAnimationDuration animations:^{
anObject.alpha = 1.0;
}];
}
}];
}
When the view rotates back to portrait, it only displays half of the scrollview, even though my NSLogs state that the frame & bounds are correct...
Am I rotating / repositioning objects correctly or is there an easier/preferred way to support landscape?
Thanks
You are explicitly setting the height to "150" when you rotate to Landscape, but don't adjust it back when you go back to portrait.
I don't know how your auto-sizing is set-up in IB, so I can't state how these objects will resize/move when left by themselves.
3 points:
you don't need to explicitly begin an animation, system will do it anyway (unless your animation is "different from rotation."
I think overriding layoutSubviews is more convenient. It's called after each rotation, and [device orientation] gives you the new orientation.
You probably want to change the frame of these views, not their bounds, which is an internal value (i.e. it matters only "inside" that view).
frame says 'put me in this position of the superivew', which is what you want. bound defines the coordinate your, say, scrollView uses for its subviews. what you need here is the frame.
In layoutSubviews, just change the frame, the animation you want will apply automatically:
- (void)layoutSubviews {
if (UIInterfaceOrientationIsLandscape([[UIDevice currentDevice] orientation])) {
anObject.frame = landscapeFrame;
}
else {
anObject.frame = portraitFrame;
}
}
I have a UIScrollView. I'd like to do some animation with it, but I want to add a neat little animation that basically zooms the UIScrollView back to where it started and then I can do some UIView animation. Is that possible?
I have tried to have a button trigger one method with the scrollView.zoomToRect, and this method calls another method in a background thread that does UIView animation. The issue is that whatever I do, the UIScrollView will just not zoom out to normal if I have an animation after it. I just want to make it zoom out followed by some animation, but I cannot.
It does not help to insert the scrollView.zoomToRect method in the animation block.
Anyone have an idea?
I am not sure if this qualifies as an answer to my own question, but in case anyone else was wondering, or in case anyone else have a better solution. I used the following code:
(Called when I hit the flip button)
- (void) flipCurrentViewZoomOut {
// If either view is zoomed in
if (view1.scrollView.zoomScale != 1 || view2.scrollView.zoomScale != 1 ) {
if (view1IsVisible == YES) {
[view1.scrollView zoomToRect:[UIScreen mainScreen].bounds animated:YES];
} else {
[bview2.scrollView zoomToRect:[UIScreen mainScreen].bounds animated:YES];
}
[UIView beginAnimations:nil context:nil];
// The duration is enough time for the zoom-out to happen before the second animation methods gets called (flipCurrentView).
[UIView setAnimationDuration:0.4];
[UIView setAnimationDelegate:self];
// When done, then do the actual flipping of the views (exchange subviews, etc.)
[UIView setAnimationDidStopSelector:#selector(flipCurrentView)];
// In order for the zoomToRect to run at all, I need to do some animation, so I basically just move the view 0.01 which is not noticable (and it's animating a flip right after anyway)
if (view1IsVisible == YES) {
view1.frame = CGRectMake(-0.01, 0, 320, 480);
} else {
view2.frame = CGRectMake(-0.01, 0, 320, 480);
}
[UIView commitAnimations];
} else {
// If either view hasn't been zoomed, the flip animation is called immediately
[self flipCurrentView];
}
}
An important thing to note is that in my flipCurrentView method (the second animation method that flips the views), I reset the frames for view1 and view2 to [UIScreen mainScreen].bounds (in this case that's the bounds I need). I have to do this, otherwise the animation code I pasted above won't run a second time, because the origin.x will then be -0.01 and it can't animate from -0.01 to -0.01, so it would have just skip that animation block.
Let me know if I am doing something completely wrong and there's a better way to do it. Always happy to learn :)