diff options
Diffstat (limited to 'src/plugins/platforms/ios/qiosviewcontroller.mm')
-rw-r--r-- | src/plugins/platforms/ios/qiosviewcontroller.mm | 115 |
1 files changed, 34 insertions, 81 deletions
diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm index c4e8968232..436d1e7bed 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.mm +++ b/src/plugins/platforms/ios/qiosviewcontroller.mm @@ -21,9 +21,12 @@ #include "qioswindow.h" #include "quiview.h" +#include <QtCore/qpointer.h> + // ------------------------------------------------------------------------- @interface QIOSViewController () +@property (nonatomic, assign) UIWindow *window; @property (nonatomic, assign) QPointer<QT_PREPEND_NAMESPACE(QIOSScreen)> platformScreen; @property (nonatomic, assign) BOOL changingOrientation; @end @@ -88,27 +91,30 @@ { Q_UNUSED(subview); - QT_PREPEND_NAMESPACE(QIOSScreen) *screen = self.qtViewController.platformScreen; - - // The 'window' property of our view is not valid until the window - // has been shown, so we have to access it through the QIOSScreen. - UIWindow *uiWindow = screen->uiWindow(); + // Track UIWindow via explicit property on QIOSViewController, + // as the window property of our own view is not valid until + // the window has been shown (below). + UIWindow *uiWindow = self.qtViewController.window; if (uiWindow.hidden) { - // Associate UIWindow to screen and show it the first time a QWindow - // is mapped to the screen. For external screens this means disabling - // mirroring mode and presenting alternate content on the screen. - uiWindow.screen = screen->uiScreen(); + // Show the UIWindow the first time a QWindow is mapped to the screen. + // For the main screen this hides the launch screen, while for external + // screens this disables mirroring of the main screen, so the external + // screen can be used for alternate content. uiWindow.hidden = NO; } } +#if !defined(Q_OS_VISIONOS) - (void)willRemoveSubview:(UIView *)subview { Q_UNUSED(subview); - Q_ASSERT(self.window); UIWindow *uiWindow = self.window; + // uiWindow can be null when closing from the ios "app manager" and the app is + // showing a native window like UIDocumentBrowserViewController + if (!uiWindow) + return; if (uiWindow.screen != [UIScreen mainScreen] && self.subviews.count == 1) { // We're about to remove the last view of an external screen, so go back @@ -116,10 +122,10 @@ // to ensure that we don't try to layout the view that's being removed. dispatch_async(dispatch_get_main_queue(), ^{ uiWindow.hidden = YES; - uiWindow.screen = [UIScreen mainScreen]; }); } } +#endif - (void)layoutSubviews { @@ -225,15 +231,14 @@ @synthesize preferredStatusBarStyle; #endif -- (instancetype)initWithQIOSScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen +- (instancetype)initWithWindow:(UIWindow*)window andScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen { if (self = [self init]) { + self.window = window; self.platformScreen = screen; self.changingOrientation = NO; #ifndef Q_OS_TVOS - self.lockedOrientation = UIInterfaceOrientationUnknown; - // Status bar may be initially hidden at startup through Info.plist self.prefersStatusBarHidden = infoPlistValue(@"UIStatusBarHidden", false); self.preferredStatusBarUpdateAnimation = UIStatusBarAnimationNone; @@ -280,7 +285,7 @@ Q_ASSERT(!qt_apple_isApplicationExtension()); -#ifndef Q_OS_TVOS +#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS) NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center addObserver:self selector:@selector(willChangeStatusBarFrame:) name:UIApplicationWillChangeStatusBarFrameNotification @@ -290,6 +295,15 @@ name:UIApplicationDidChangeStatusBarOrientationNotification object:qt_apple_sharedApplication()]; #endif + + // Make sure any top level windows that have already been created + // for this screen are reparented into our desktop manager view. + for (auto *window : qGuiApp->topLevelWindows()) { + if (window->screen()->handle() != self.platformScreen) + continue; + if (auto *platformWindow = window->handle()) + platformWindow->setParent(nullptr); + } } - (void)viewDidUnload @@ -300,26 +314,6 @@ // ------------------------------------------------------------------------- -- (BOOL)shouldAutorotate -{ -#ifndef Q_OS_TVOS - return self.platformScreen && self.platformScreen->uiScreen() == [UIScreen mainScreen] && !self.lockedOrientation; -#else - return NO; -#endif -} - -- (NSUInteger)supportedInterfaceOrientations -{ - // 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; -} - - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration { self.changingOrientation = YES; @@ -334,6 +328,7 @@ [super didRotateFromInterfaceOrientation:orientation]; } +#if !defined(Q_OS_VISIONOS) - (void)willChangeStatusBarFrame:(NSNotification*)notification { Q_UNUSED(notification); @@ -377,6 +372,7 @@ [self.view setNeedsLayout]; } +#endif - (void)viewWillLayoutSubviews { @@ -397,10 +393,12 @@ if (!self.platformScreen || !self.platformScreen->screen()) return; +#if !defined(Q_OS_VISIONOS) // For now we only care about the main screen, as both the statusbar // visibility and orientation is only appropriate for the main screen. if (self.platformScreen->uiScreen() != [UIScreen mainScreen]) return; +#endif // Prevent recursion caused by updating the status bar appearance (position // or visibility), which in turn may cause a layout of our subviews, and @@ -427,7 +425,7 @@ // All decisions are based on the top level window focusWindow = qt_window_private(focusWindow)->topLevelWindow(); -#ifndef Q_OS_TVOS +#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS) // -------------- Status bar style and visbility --------------- @@ -447,51 +445,6 @@ [self setNeedsStatusBarAppearanceUpdate]; [self.view setNeedsLayout]; } - - - // -------------- Content orientation --------------- - - UIApplication *uiApplication = qt_apple_sharedApplication(); - - 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.lockedOrientation) { - // 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; - } - - [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.lockedOrientation) { - // 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:self.lockedOrientation - animated:kAnimateContentOrientationChanges]; - - // Then we can re-enable auto-rotation - self.lockedOrientation = UIInterfaceOrientationUnknown; - - // And finally let iOS rotate the root view to match the device orientation - [UIViewController attemptRotationToDeviceOrientation]; - } - } #endif } |