44

I have a UIView which is supposed to cover the whole device (UIWindow) to support an image zoom in/out effect I'm doing using core animation where a user taps a button on a UITableViewCell and I zoom the associated image.

The zooming is performing flawlessly, what I haven't been able to figure out is why the subview is still in portrait mode even though the device is in landscape. An illustration below:

enter image description here

I do have a navigation controller but this view has been added to the UIWindow directly.

Kev
  • 118,037
  • 53
  • 300
  • 385
Ken
  • 773
  • 1
  • 9
  • 17
  • Does this answer help at all? http://stackoverflow.com/a/3368786/419 if so could you accept that instead? – Kev Jul 21 '12 at 23:01

6 Answers6

121

You can read about some of the possible causes here:
Technical Q&A QA1688 - Why won't my UIViewController rotate with the device?

In your situation its probably the fact that you are adding the view as another subview to the window. Only the first subview gets the rotation events. What you can do is add it as a subview of the first window subview.

UIWindow* window = [UIApplication sharedApplication].keyWindow;
if (!window) 
    window = [[UIApplication sharedApplication].windows objectAtIndex:0];
[[[window subviews] objectAtIndex:0] addSubview:myView];    
ThomasW
  • 16,981
  • 4
  • 79
  • 106
dizy
  • 7,951
  • 10
  • 53
  • 54
  • That's amazing - thanks for pointing the QA link. In official docs, it's undocumented (I've scoured the View and Window programming guides, and the UIWindow and UIView classes, couldn't find any mention of it), and clearly a massive bug (that Apple has no intention of fixing, judging by the tone of the QA article). – Adam Jan 13 '11 at 13:20
  • 14
    Adding to the first subview won't work if you want to show in front of a modal view controller (at least from iOS5 onwards) as modal view controllers are added as a second view hierarchy on the main UIWindow subviews. – Fábio Oliveira Sep 18 '12 at 16:27
  • @FábioOliveira :- +1, Thanks for highlighting the drawback in this, i will keep this also in my mind. :) – mAc Jan 22 '13 at 13:35
  • but if our application has UINavigationController, its showing NavigationBar on top of myView. Why this? – Bhushan B May 13 '13 at 09:47
  • Hey @FábioOliveira, have you figured out any workaround on the situation you said? – Kjuly May 28 '13 at 02:04
  • @Fabio agree. i am adding a subview in a subview added to view. It adds as a sibling of first subview, not as a child. – khunshan Aug 12 '14 at 09:32
  • The link mentioned here is "Why won't my UIViewController rotate with the device?". What the question is "Why won't my UIView rotate with the device?" I am sad that it is about UIView property of UIViewController. And encourages about one rootViewController. My application has one container vew controller as rootViewController. But the problem is UIView added to UIWindow is not working. Its an independent Subview extended from UIView. That is completely separate case. – khunshan Aug 12 '14 at 12:31
  • 1
    you may get count = 0 when [window subviews] – ikzjfr0 Oct 16 '14 at 04:00
  • window = [[UIApplication sharedApplication].windows objectAtIndex:0]; on iOS7, in landscape is still in portrait (frame is 768x1024 on iPad). And the bounds are also 768x1024 and the transform is the identity transform. – Chris Prince Jun 25 '15 at 21:14
83

The problem

Beginning with iOS 6, only the topmost view controller (alongside the UIApplication object) participates in deciding whether to rotate in response to a change of the device's orientation.

https://developer.apple.com/library/content/qa/qa1688/_index.html

The solution

I have open sourced a pod named AGWindowView.
It will automatically deal with any rotation and framechanges so you won't have to worry about that.

The code

It supports any combination of SDK's and iOS system versions. The relevant code can be found here: https://github.com/hfossli/AGWindowView/blob/master/Source/AGWindowView.m

Cœur
  • 37,241
  • 25
  • 195
  • 267
hfossli
  • 22,616
  • 10
  • 116
  • 130
  • 1
    The view seems to resize to fill the window on rotation. How do you maintain the original size? – jumponadoughnut Jun 19 '13 at 14:54
  • That's the point. :) If you want a specific view to maintain the original size then I suggest you add it as a subview. – hfossli Jun 19 '13 at 16:47
  • FYI, there's a bunch of stuff in the code above that references methods not found in standard ios libraries. I assume things like UIInterfaceOrientationAngleOfOrientation are found in the full code that hfossli wrote. – Ben Wheeler Jun 24 '13 at 21:13
  • @BenjaminWheeler Ok. You are free to edit or update. The function you mention is already in this post. – hfossli Jun 25 '13 at 09:00
5

I created a category on UIApplication that has a helper property and method for getting the first subview of the keyWindow. This is the view you want to overlay anyway. Now when you add a view that is managed by a UIViewController to that view, the shouldRotateToInterfaceOrientation: method is called.

UIApplication+WindowOverlay.h

#import <UIKit/UIKit.h>

@interface UIApplication(WindowOverlay)

    @property (nonatomic, readonly) UIView *baseWindowView;

    -(void)addWindowOverlay:(UIView *)view;

@end

UIApplication+WindowOverlay.m

#import "UIApplication+WindowOverlay.h"

@implementation UIApplication(WindowOverlay)

    -(UIView *)baseWindowView{
        if (self.keyWindow.subviews.count > 0){
            return [self.keyWindow.subviews objectAtIndex:0];
        }
        return nil;
    }

    -(void)addWindowOverlay:(UIView *)view{
        [self.baseWindowView addSubview:view];
    }
@end

and here is how you would use it.

//at the top of the file...or in {yourproject}.pch
#import "UIApplication+WindowOverlay.h

//in a method:

UIView *view = [UIView new];

UIView *window = [UIApplication sharedApplication].baseWindowView;

view.frame = window.bounds;

[window addSubview:view];

//or

[[UIApplication sharedApplication] addWindowOverlay:view];
Jon
  • 462
  • 4
  • 13
  • What about it getting the orientation change events? – Fábio Oliveira Sep 18 '12 at 14:48
  • your view will be rotated and the autoresize mask will be honored if you use this. it is based on what should have been the accepted answer from above. I don't have an answer for getting rotation events specifically, but i'm sure it's been answered somewhere else. – Jon Sep 19 '12 at 14:20
  • 1
    Ok. I missed the part that was a subview of the window's first subview. Then a problem with this approach is that you cannot present it from a modal view controller. Maybe if you add it as a subview of the last window's subviews. This way if you have a modal view controller showing you have two subviews on the main window: the root view controller view and the dimming view of the modal view controller and you want to add it to the dimming view, so it shows on top. – Fábio Oliveira Sep 20 '12 at 07:39
  • this wont work on iOS 7 if you have an UIAlertView up as that will be the key window. [[UIApplication sharedApplication] windows][0] will always return the Application's window. – odyth Jan 10 '14 at 08:18
1

This is because as you mention your view has been added directly to the UIWindow, therefore when the method to rotate is called for the navigation controller nothing happens to the uiview. The UIView would rotate if it was a subview of the view controller view. If for some reason this cannot be done. Then you could override this method:

// This method is called every time the device changes orientation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
}

And every time your orientation changes also change your view orientation.

Oscar Gomez
  • 18,436
  • 13
  • 85
  • 118
0

Another solution how I solved this problem.

Define the current Orientation:

@interface AJImageCollectionViewController (){
    UIInterfaceOrientation _currentOrientation;
}
@end

Then check the orientation in the viewWillLayoutSubviews:

- (void)viewWillLayoutSubviews {
    [self checkIfOrientationChanged];
}

- (void)checkIfOrientationChanged {
    UIInterfaceOrientation newOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    BOOL newOrientationIsPortrait = UIInterfaceOrientationIsPortrait(newOrientation);
    BOOL oldOrientationIsPortrait = UIInterfaceOrientationIsPortrait(_currentOrientation);

    // Check if the orientation is the same as the current
    if(newOrientationIsPortrait != oldOrientationIsPortrait){
        _currentOrientation = newOrientation;
        // Do some stuff with the new orientation
    }
}
Antoine
  • 23,526
  • 11
  • 88
  • 94
0

I had a similar problem with views being added directly to a window. Maybe this will help: Automatically Sizing UIView after Adding to Window

Community
  • 1
  • 1
Kristopher Johnson
  • 81,409
  • 55
  • 245
  • 302