summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/ios/qiosviewcontroller.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/ios/qiosviewcontroller.mm')
-rw-r--r--src/plugins/platforms/ios/qiosviewcontroller.mm169
1 files changed, 164 insertions, 5 deletions
diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm
index 2fe679fc20..1c06228e81 100644
--- a/src/plugins/platforms/ios/qiosviewcontroller.mm
+++ b/src/plugins/platforms/ios/qiosviewcontroller.mm
@@ -50,9 +50,129 @@
#include "qiosscreen.h"
#include "qiosglobal.h"
#include "qioswindow.h"
+#include "quiview.h"
+
+// -------------------------------------------------------------------------
+
+@interface QIOSDesktopManagerView : UIView
+@end
+
+@implementation QIOSDesktopManagerView
+
+- (void)layoutSubviews
+{
+ for (int i = int(self.subviews.count) - 1; i >= 0; --i) {
+ UIView *view = static_cast<UIView *>([self.subviews objectAtIndex:i]);
+ if (![view isKindOfClass:[QUIView class]])
+ continue;
+
+ [self layoutView: static_cast<QUIView *>(view)];
+ }
+}
+
+- (void)layoutView:(QUIView *)view
+{
+ QWindow *window = view.qwindow;
+ Q_ASSERT(window->handle());
+
+ // Re-apply window states to update geometry
+ if (window->windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized))
+ window->handle()->setWindowState(window->windowState());
+}
+
+// Even if the root view controller has both wantsFullScreenLayout and
+// extendedLayoutIncludesOpaqueBars enabled, iOS will still push the root
+// view down 20 pixels (and shrink the view accordingly) when the in-call
+// statusbar is active (instead of updating the topLayoutGuide). Since
+// we treat the root view controller as our screen, we want to reflect
+// the in-call statusbar as a change in available geometry, not in screen
+// geometry. To simplify the screen geometry mapping code we reset the
+// view modifications that iOS does and take the statusbar height
+// explicitly into account in QIOSScreen::updateProperties().
+
+- (void)setFrame:(CGRect)newFrame
+{
+ [super setFrame:CGRectMake(0, 0, CGRectGetWidth(newFrame), CGRectGetHeight(self.window.bounds))];
+}
+
+- (void)setBounds:(CGRect)newBounds
+{
+ CGRect transformedWindowBounds = [self convertRect:self.window.bounds fromView:self.window];
+ [super setBounds:CGRectMake(0, 0, CGRectGetWidth(newBounds), CGRectGetHeight(transformedWindowBounds))];
+}
+
+- (void)setCenter:(CGPoint)newCenter
+{
+ Q_UNUSED(newCenter);
+ [super setCenter:self.window.center];
+}
+
+- (void)didMoveToWindow
+{
+ // The initial frame computed during startup may happen before the view has
+ // a window, meaning our calculations above will be wrong. We ensure that the
+ // frame is set correctly once we have a window to base our calulations on.
+ [self setFrame:self.window.bounds];
+}
+
+@end
+
+// -------------------------------------------------------------------------
@implementation QIOSViewController
+- (id)initWithQIOSScreen:(QIOSScreen *)screen
+{
+ if (self = [self init]) {
+ m_screen = screen;
+
+#if QT_IOS_DEPLOYMENT_TARGET_BELOW(__IPHONE_7_0)
+ QSysInfo::MacVersion iosVersion = QSysInfo::MacintoshVersion;
+
+ // We prefer to keep the root viewcontroller in fullscreen layout, so that
+ // we don't have to compensate for the viewcontroller position. This also
+ // gives us the same behavior on iOS 5/6 as on iOS 7, where full screen layout
+ // is the only way.
+ if (iosVersion < QSysInfo::MV_IOS_7_0)
+ self.wantsFullScreenLayout = YES;
+
+ // Use translucent statusbar by default on iOS6 iPhones (unless the user changed
+ // the default in the Info.plist), so that windows placed under the stausbar are
+ // still visible, just like on iOS7.
+ if (screen->uiScreen() == [UIScreen mainScreen]
+ && iosVersion >= QSysInfo::MV_IOS_6_0 && iosVersion < QSysInfo::MV_IOS_7_0
+ && [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone
+ && [UIApplication sharedApplication].statusBarStyle == UIStatusBarStyleDefault)
+ [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent];
+#endif
+
+ self.changingOrientation = NO;
+ }
+
+ return self;
+}
+
+- (void)loadView
+{
+ self.view = [[[QIOSDesktopManagerView alloc] init] autorelease];
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserver:self selector:@selector(willChangeStatusBarFrame:)
+ name:UIApplicationWillChangeStatusBarFrameNotification
+ object:[UIApplication sharedApplication]];
+}
+
+- (void)viewDidUnload
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:nil];
+ [super viewDidUnload];
+}
+
-(BOOL)shouldAutorotate
{
// Until a proper orientation and rotation API is in place, we always auto rotate.
@@ -77,16 +197,55 @@
}
#endif
-- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
+- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
{
+ Q_UNUSED(orientation);
Q_UNUSED(duration);
- Q_UNUSED(interfaceOrientation);
+ self.changingOrientation = YES;
+}
+
+- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)orientation
+{
+ Q_UNUSED(orientation);
+
+ self.changingOrientation = NO;
+}
+
+- (void)willChangeStatusBarFrame:(NSNotification*)notification
+{
+ Q_UNUSED(notification);
+
+ if (self.view.window.screen != [UIScreen mainScreen])
+ return;
+
+ // Orientation changes will already result in laying out subviews, so we don't
+ // need to do anything extra for frame changes during an orientation change.
+ // Technically we can receive another actual statusbar frame update during the
+ // orientation change that we should react to, but to simplify the logic we
+ // use a simple bool variable instead of a ignoreNextFrameChange approach.
+ if (self.changingOrientation)
+ return;
+
+ // UIKit doesn't have a delegate callback for statusbar changes that's run inside the
+ // animation block, like UIViewController's willAnimateRotationToInterfaceOrientation,
+ // nor does it expose a constant for the duration and easing of the animation. However,
+ // though poking at the various UIStatusBar methods, we can observe that the animation
+ // uses the default easing curve, and runs with a duration of 0.35 seconds.
+ static qreal kUIStatusBarAnimationDuration = 0.35;
+
+ [UIView animateWithDuration:kUIStatusBarAnimationDuration animations:^{
+ [self.view setNeedsLayout];
+ [self.view layoutIfNeeded];
+ }];
+}
+
+- (void)viewWillLayoutSubviews
+{
if (!QCoreApplication::instance())
- return; // FIXME: Store orientation for later (?)
+ return;
- QIOSScreen *qiosScreen = static_cast<QIOSScreen *>(QGuiApplication::primaryScreen()->handle());
- qiosScreen->updateProperties();
+ m_screen->updateProperties();
}
#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0)