diff options
Diffstat (limited to 'src/plugins/platforms/ios/qiosviewcontroller.mm')
-rw-r--r-- | src/plugins/platforms/ios/qiosviewcontroller.mm | 137 |
1 files changed, 134 insertions, 3 deletions
diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm index 73f1b51b83..1c06228e81 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.mm +++ b/src/plugins/platforms/ios/qiosviewcontroller.mm @@ -50,6 +50,74 @@ #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 @@ -77,11 +145,34 @@ && [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. @@ -106,13 +197,53 @@ } #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; m_screen->updateProperties(); } |