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:
parent
5c5f43e95b
commit
95cb745e00
@ -46,6 +46,7 @@
|
|||||||
#include <qpa/qwindowsysteminterface.h>
|
#include <qpa/qwindowsysteminterface.h>
|
||||||
#include "qiosapplicationdelegate.h"
|
#include "qiosapplicationdelegate.h"
|
||||||
#include "qiosviewcontroller.h"
|
#include "qiosviewcontroller.h"
|
||||||
|
#include "quiview.h"
|
||||||
|
|
||||||
#include <sys/sysctl.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_geometry = fromCGRect([rootView convertRect:m_uiScreen.bounds fromView:m_uiWindow]).toRect();
|
||||||
m_availableGeometry = fromCGRect([rootView convertRect:m_uiScreen.applicationFrame 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) {
|
if (m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry) {
|
||||||
const qreal millimetersPerInch = 25.4;
|
const qreal millimetersPerInch = 25.4;
|
||||||
m_physicalSize = QSizeF(m_geometry.size()) / m_unscaledDpi * millimetersPerInch;
|
m_physicalSize = QSizeF(m_geometry.size()) / m_unscaledDpi * millimetersPerInch;
|
||||||
|
@ -40,6 +40,10 @@ class QIOSScreen;
|
|||||||
- (id)initWithQIOSScreen:(QIOSScreen *)screen;
|
- (id)initWithQIOSScreen:(QIOSScreen *)screen;
|
||||||
- (void)updateProperties;
|
- (void)updateProperties;
|
||||||
|
|
||||||
|
@property (nonatomic, assign) UIInterfaceOrientation lockedOrientation;
|
||||||
|
|
||||||
|
// UIViewController
|
||||||
|
@property (nonatomic, assign) BOOL shouldAutorotate;
|
||||||
@property (nonatomic, assign) BOOL prefersStatusBarHidden;
|
@property (nonatomic, assign) BOOL prefersStatusBarHidden;
|
||||||
@property (nonatomic, assign) UIStatusBarAnimation preferredStatusBarUpdateAnimation;
|
@property (nonatomic, assign) UIStatusBarAnimation preferredStatusBarUpdateAnimation;
|
||||||
|
|
||||||
|
@ -153,6 +153,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
self.changingOrientation = NO;
|
self.changingOrientation = NO;
|
||||||
|
self.shouldAutorotate = [super shouldAutorotate];
|
||||||
|
|
||||||
// Status bar may be initially hidden at startup through Info.plist
|
// Status bar may be initially hidden at startup through Info.plist
|
||||||
self.prefersStatusBarHidden = infoPlistValue(@"UIStatusBarHidden", false);
|
self.prefersStatusBarHidden = infoPlistValue(@"UIStatusBarHidden", false);
|
||||||
@ -179,6 +180,10 @@
|
|||||||
[center addObserver:self selector:@selector(willChangeStatusBarFrame:)
|
[center addObserver:self selector:@selector(willChangeStatusBarFrame:)
|
||||||
name:UIApplicationWillChangeStatusBarFrameNotification
|
name:UIApplicationWillChangeStatusBarFrameNotification
|
||||||
object:[UIApplication sharedApplication]];
|
object:[UIApplication sharedApplication]];
|
||||||
|
|
||||||
|
[center addObserver:self selector:@selector(didChangeStatusBarOrientation:)
|
||||||
|
name:UIApplicationDidChangeStatusBarOrientationNotification
|
||||||
|
object:[UIApplication sharedApplication]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewDidUnload
|
- (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)
|
#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_6_0)
|
||||||
-(NSUInteger)supportedInterfaceOrientations
|
-(NSUInteger)supportedInterfaceOrientations
|
||||||
{
|
{
|
||||||
// We need to tell iOS that we support all orientations in order to set
|
// As documented by Apple in the iOS 6.0 release notes, setStatusBarOrientation:animated:
|
||||||
// status bar orientation when application content orientation changes.
|
// only works if the supportedInterfaceOrientations of the view controller is 0, making
|
||||||
return UIInterfaceOrientationMaskAll;
|
// 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
|
#endif
|
||||||
|
|
||||||
@ -209,7 +211,7 @@
|
|||||||
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
|
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
|
||||||
{
|
{
|
||||||
Q_UNUSED(interfaceOrientation);
|
Q_UNUSED(interfaceOrientation);
|
||||||
return YES;
|
return [self shouldAutorotate];
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
- (void)viewWillLayoutSubviews
|
||||||
{
|
{
|
||||||
if (!QCoreApplication::instance())
|
if (!QCoreApplication::instance())
|
||||||
@ -287,6 +305,8 @@
|
|||||||
// All decisions are based on the the top level window
|
// All decisions are based on the the top level window
|
||||||
focusWindow = qt_window_private(focusWindow)->topLevelWindow();
|
focusWindow = qt_window_private(focusWindow)->topLevelWindow();
|
||||||
|
|
||||||
|
UIApplication *uiApplication = [UIApplication sharedApplication];
|
||||||
|
|
||||||
bool currentStatusBarVisibility = self.prefersStatusBarHidden;
|
bool currentStatusBarVisibility = self.prefersStatusBarHidden;
|
||||||
self.prefersStatusBarHidden = focusWindow->windowState() == Qt::WindowFullScreen;
|
self.prefersStatusBarHidden = focusWindow->windowState() == Qt::WindowFullScreen;
|
||||||
|
|
||||||
@ -297,13 +317,60 @@
|
|||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
[[UIApplication sharedApplication]
|
[uiApplication setStatusBarHidden:self.prefersStatusBarHidden
|
||||||
setStatusBarHidden:self.prefersStatusBarHidden
|
|
||||||
withAnimation:self.preferredStatusBarUpdateAnimation];
|
withAnimation:self.preferredStatusBarUpdateAnimation];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self.view setNeedsLayout];
|
[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)
|
#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0)
|
||||||
|
@ -75,6 +75,15 @@ QIOSWindow::QIOSWindow(QWindow *window)
|
|||||||
|
|
||||||
setWindowState(window->windowState());
|
setWindowState(window->windowState());
|
||||||
setOpacity(window->opacity());
|
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()
|
QIOSWindow::~QIOSWindow()
|
||||||
@ -322,10 +331,12 @@ void QIOSWindow::updateWindowLevel()
|
|||||||
|
|
||||||
void QIOSWindow::handleContentOrientationChange(Qt::ScreenOrientation orientation)
|
void QIOSWindow::handleContentOrientationChange(Qt::ScreenOrientation orientation)
|
||||||
{
|
{
|
||||||
// Keep the status bar in sync with content orientation. This will ensure
|
// Update the QWindow representation straight away, so that
|
||||||
// that the task bar (and associated gestures) are aligned correctly:
|
// we can update the statusbar orientation based on the new
|
||||||
UIInterfaceOrientation uiOrientation = UIInterfaceOrientation(fromQtScreenOrientation(orientation));
|
// content orientation.
|
||||||
[[UIApplication sharedApplication] setStatusBarOrientation:uiOrientation animated:NO];
|
qt_window_private(window())->contentOrientation = orientation;
|
||||||
|
|
||||||
|
[m_view.qtViewController updateProperties];
|
||||||
}
|
}
|
||||||
|
|
||||||
void QIOSWindow::applicationStateChanged(Qt::ApplicationState)
|
void QIOSWindow::applicationStateChanged(Qt::ApplicationState)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user