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 "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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user