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>
This commit is contained in:
Tor Arne Vestbø 2013-12-11 14:37:19 +01:00 committed by Tor Arne Vestbø
parent 5c5f43e95b
commit 95cb745e00
4 changed files with 116 additions and 17 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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)