diff options
author | Tor Arne Vestbø <tor.arne.vestbo@digia.com> | 2013-12-11 14:37:19 +0100 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@theqtcompany.com> | 2015-01-09 11:22:59 +0100 |
commit | 95cb745e002c38e821b385e90d954adf73363e3c (patch) | |
tree | 612d068ebd27c694a6cf52a6fa8f1987d8465008 /src/plugins/platforms/ios | |
parent | 5c5f43e95b8f56d2bdf34cf5c11aae9d5102f9c6 (diff) |
iOS: Fix QWindow::reportContentOrientationChange on iOS6+
On iOS 6 and above, [UIViewController supportedInterfaceOrientations]
needs to return 0 for [UIApplication setStatusBarOrientation] to work.
This means once you report a content orientation other than the primary
orientation, you'll disable auto-rotation. Reporting the orientation as
Qt::PrimaryOrientation restores the auto-rotation behavior.
Change-Id: I1b8c765c507728fdbc5b828e0b4215324014e221
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@theqtcompany.com>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@theqtcompany.com>
Diffstat (limited to 'src/plugins/platforms/ios')
-rw-r--r-- | src/plugins/platforms/ios/qiosscreen.mm | 17 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosviewcontroller.h | 4 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosviewcontroller.mm | 93 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qioswindow.mm | 19 |
4 files changed, 116 insertions, 17 deletions
diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm index 4af2a4965f..712bf0098b 100644 --- a/src/plugins/platforms/ios/qiosscreen.mm +++ b/src/plugins/platforms/ios/qiosscreen.mm @@ -46,6 +46,7 @@ #include <qpa/qwindowsysteminterface.h> #include "qiosapplicationdelegate.h" #include "qiosviewcontroller.h" +#include "quiview.h" #include <sys/sysctl.h> @@ -244,6 +245,22 @@ void QIOSScreen::updateProperties() m_geometry = fromCGRect([rootView convertRect:m_uiScreen.bounds fromView:m_uiWindow]).toRect(); m_availableGeometry = fromCGRect([rootView convertRect:m_uiScreen.applicationFrame fromView:m_uiWindow]).toRect(); + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_8_0 && ![m_uiWindow.rootViewController shouldAutorotate]) { + // Setting the statusbar orientation (content orientation) on iOS8+ will result in the UIScreen + // updating its geometry and available geometry, which in the case of content orientation is not + // what we want. We want to reflect the screen geometry based on the locked orientation, and + // adjust the available geometry based on the repositioned status bar for the current status + // bar orientation. + + Qt::ScreenOrientation lockedOrientation = toQtScreenOrientation(UIDeviceOrientation(rootView.qtViewController.lockedOrientation)); + Qt::ScreenOrientation contenOrientation = toQtScreenOrientation(UIDeviceOrientation([UIApplication sharedApplication].statusBarOrientation)); + + QTransform transform = screen()->transformBetween(lockedOrientation, contenOrientation, m_geometry).inverted(); + + m_geometry = transform.mapRect(m_geometry); + m_availableGeometry = transform.mapRect(m_availableGeometry); + } + if (m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry) { const qreal millimetersPerInch = 25.4; m_physicalSize = QSizeF(m_geometry.size()) / m_unscaledDpi * millimetersPerInch; diff --git a/src/plugins/platforms/ios/qiosviewcontroller.h b/src/plugins/platforms/ios/qiosviewcontroller.h index 684e06e030..df7ce0ff4a 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.h +++ b/src/plugins/platforms/ios/qiosviewcontroller.h @@ -40,6 +40,10 @@ class QIOSScreen; - (id)initWithQIOSScreen:(QIOSScreen *)screen; - (void)updateProperties; +@property (nonatomic, assign) UIInterfaceOrientation lockedOrientation; + +// UIViewController +@property (nonatomic, assign) BOOL shouldAutorotate; @property (nonatomic, assign) BOOL prefersStatusBarHidden; @property (nonatomic, assign) UIStatusBarAnimation preferredStatusBarUpdateAnimation; diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm index 8f59d41509..0afd2bf8ba 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.mm +++ b/src/plugins/platforms/ios/qiosviewcontroller.mm @@ -153,6 +153,7 @@ #endif self.changingOrientation = NO; + self.shouldAutorotate = [super shouldAutorotate]; // Status bar may be initially hidden at startup through Info.plist self.prefersStatusBarHidden = infoPlistValue(@"UIStatusBarHidden", false); @@ -179,6 +180,10 @@ [center addObserver:self selector:@selector(willChangeStatusBarFrame:) name:UIApplicationWillChangeStatusBarFrameNotification object:[UIApplication sharedApplication]]; + + [center addObserver:self selector:@selector(didChangeStatusBarOrientation:) + name:UIApplicationDidChangeStatusBarOrientationNotification + object:[UIApplication sharedApplication]]; } - (void)viewDidUnload @@ -189,19 +194,16 @@ // ------------------------------------------------------------------------- --(BOOL)shouldAutorotate -{ - // Until a proper orientation and rotation API is in place, we always auto rotate. - // If auto rotation is not wanted, you would need to switch it off manually from Info.plist. - return YES; -} - #if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_6_0) -(NSUInteger)supportedInterfaceOrientations { - // We need to tell iOS that we support all orientations in order to set - // status bar orientation when application content orientation changes. - return UIInterfaceOrientationMaskAll; + // As documented by Apple in the iOS 6.0 release notes, setStatusBarOrientation:animated: + // only works if the supportedInterfaceOrientations of the view controller is 0, making + // us responsible for ensuring that the status bar orientation is consistent. We enter + // this mode when auto-rotation is disabled due to an explicit content orientation being + // set on the focus window. Note that this is counter to what the documentation for + // supportedInterfaceOrientations says, which states that the method should not return 0. + return [self shouldAutorotate] ? UIInterfaceOrientationMaskAll : 0; } #endif @@ -209,7 +211,7 @@ -(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { Q_UNUSED(interfaceOrientation); - return YES; + return [self shouldAutorotate]; } #endif @@ -256,6 +258,22 @@ }]; } +- (void)didChangeStatusBarOrientation:(NSNotification *)notification +{ + Q_UNUSED(notification); + + if (self.view.window.screen != [UIScreen mainScreen]) + return; + + // If the statusbar changes orientation due to auto-rotation we don't care, + // there will be re-layout anyways. Only if the statusbar changes due to + // reportContentOrientation, we need to update the window layout. + if (self.changingOrientation) + return; + + [self.view setNeedsLayout]; +} + - (void)viewWillLayoutSubviews { if (!QCoreApplication::instance()) @@ -287,6 +305,8 @@ // All decisions are based on the the top level window focusWindow = qt_window_private(focusWindow)->topLevelWindow(); + UIApplication *uiApplication = [UIApplication sharedApplication]; + bool currentStatusBarVisibility = self.prefersStatusBarHidden; self.prefersStatusBarHidden = focusWindow->windowState() == Qt::WindowFullScreen; @@ -297,13 +317,60 @@ } else #endif { - [[UIApplication sharedApplication] - setStatusBarHidden:self.prefersStatusBarHidden + [uiApplication setStatusBarHidden:self.prefersStatusBarHidden withAnimation:self.preferredStatusBarUpdateAnimation]; } [self.view setNeedsLayout]; } + + + // -------------- Content orientation --------------- + + static BOOL kAnimateContentOrientationChanges = YES; + + Qt::ScreenOrientation contentOrientation = focusWindow->contentOrientation(); + if (contentOrientation != Qt::PrimaryOrientation) { + // An explicit content orientation has been reported for the focus window, + // so we keep the status bar in sync with content orientation. This will ensure + // that the task bar (and associated gestures) are also rotated accordingly. + + if (self.shouldAutorotate) { + // We are moving from Qt::PrimaryOrientation to an explicit orientation, + // so we need to store the current statusbar orientation, as we need it + // later when mapping screen coordinates for QScreen and for returning + // to Qt::PrimaryOrientation. + self.lockedOrientation = uiApplication.statusBarOrientation; + + // Calling setStatusBarOrientation only has an effect when auto-rotation is + // disabled, which makes sense when there's an explicit content orientation. + self.shouldAutorotate = NO; + } + + [uiApplication setStatusBarOrientation: + UIInterfaceOrientation(fromQtScreenOrientation(contentOrientation)) + animated:kAnimateContentOrientationChanges]; + + } else { + // The content orientation is set to Qt::PrimaryOrientation, meaning + // that auto-rotation should be enabled. But we may be coming out of + // a state of locked orientation, which needs some cleanup before we + // can enable auto-rotation again. + if (!self.shouldAutorotate) { + // First we need to restore the statusbar to what it was at the + // time of locking the orientation, otherwise iOS will be very + // confused when it starts doing auto-rotation again. + [uiApplication setStatusBarOrientation: + UIInterfaceOrientation(self.lockedOrientation) + animated:kAnimateContentOrientationChanges]; + + // Then we can re-enable auto-rotation + self.shouldAutorotate = YES; + + // And finally let iOS rotate the root view to match the device orientation + [UIViewController attemptRotationToDeviceOrientation]; + } + } } #if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0) diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index 480062e4de..6c4614408d 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -75,6 +75,15 @@ QIOSWindow::QIOSWindow(QWindow *window) setWindowState(window->windowState()); setOpacity(window->opacity()); + + Qt::ScreenOrientation initialOrientation = window->contentOrientation(); + if (initialOrientation != Qt::PrimaryOrientation) { + // Start up in portrait, then apply possible content orientation, + // as per Apple's documentation. + dispatch_async(dispatch_get_main_queue(), ^{ + handleContentOrientationChange(initialOrientation); + }); + } } QIOSWindow::~QIOSWindow() @@ -322,10 +331,12 @@ void QIOSWindow::updateWindowLevel() void QIOSWindow::handleContentOrientationChange(Qt::ScreenOrientation orientation) { - // Keep the status bar in sync with content orientation. This will ensure - // that the task bar (and associated gestures) are aligned correctly: - UIInterfaceOrientation uiOrientation = UIInterfaceOrientation(fromQtScreenOrientation(orientation)); - [[UIApplication sharedApplication] setStatusBarOrientation:uiOrientation animated:NO]; + // Update the QWindow representation straight away, so that + // we can update the statusbar orientation based on the new + // content orientation. + qt_window_private(window())->contentOrientation = orientation; + + [m_view.qtViewController updateProperties]; } void QIOSWindow::applicationStateChanged(Qt::ApplicationState) |