IOS7: Resize main frame depending on keyboard size and orientation
It's important that the main frame, displaying the OpenGL rendered graphics, has the proper dimensions and depending on the device orientation. It's also important that the frame is not covered by the iOS keyboard. This commit calculates the frame size depening on the orientation and the keyboard status. The keyboard knows its parent view and can resize it when the keyboard becomes visible or hidden. There are multiple scenarios where the frame size is changed. - When the keyboard is hidden/shown which can be automatically change depending on the device orientation - If the system demands the keyboard to get visible or hidden - When rotating the device - When suspending/resuming the application There can also be combination of the scenarios above, e.g. if suspending the application in landscape mode and resume it in portrait mode. A lot of effort has been put into testing different scenarios to verify that the screen size becomes correct. However there might be some scenario which has not been covered.
This commit is contained in:
parent
5d5564fceb
commit
d8bc349b37
3 changed files with 99 additions and 9 deletions
|
@ -393,16 +393,72 @@
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@implementation SoftKeyboard
|
@implementation SoftKeyboard {
|
||||||
|
BOOL _keyboardVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TARGET_OS_IOS
|
||||||
|
- (void)resizeParentFrame:(NSNotification*)notification keyboardDidShow:(BOOL)didShow
|
||||||
|
{
|
||||||
|
NSDictionary* userInfo = [notification userInfo];
|
||||||
|
CGRect keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
||||||
|
keyboardFrame = [self.superview convertRect:keyboardFrame fromView:nil];
|
||||||
|
|
||||||
|
// Base the new frame size on the current parent frame size
|
||||||
|
CGRect newFrame = self.superview.frame;
|
||||||
|
newFrame.size.height += (keyboardFrame.size.height) * (didShow ? -1 : 1);
|
||||||
|
|
||||||
|
// Resize with a fancy animation
|
||||||
|
NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];
|
||||||
|
[UIView animateWithDuration:rate.floatValue animations:^{
|
||||||
|
self.superview.frame = newFrame;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)keyboardDidShow:(NSNotification*)notification
|
||||||
|
{
|
||||||
|
// NotificationCenter might notify multiple times
|
||||||
|
// when keyboard did show because the accessoryView
|
||||||
|
// affect the keyboard height. However since we use
|
||||||
|
// UIKeyboardFrameEndUserInfoKey to get the keyboard
|
||||||
|
// it will always have the same value. Make sure to
|
||||||
|
// only handle one notification.
|
||||||
|
if (!_keyboardVisible) {
|
||||||
|
[self resizeParentFrame:notification keyboardDidShow:YES];
|
||||||
|
_keyboardVisible = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)keyboardDidHide:(NSNotification*)notification
|
||||||
|
{
|
||||||
|
// NotificationCenter will only call this once
|
||||||
|
// when keyboard did hide.
|
||||||
|
[self resizeParentFrame:notification keyboardDidShow:NO];
|
||||||
|
_keyboardVisible = NO;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)frame {
|
- (id)initWithFrame:(CGRect)frame {
|
||||||
self = [super initWithFrame:frame];
|
self = [super initWithFrame:frame];
|
||||||
|
|
||||||
|
#if TARGET_OS_IOS
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(keyboardDidShow:)
|
||||||
|
name:UIKeyboardDidShowNotification
|
||||||
|
object:nil];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(keyboardDidHide:)
|
||||||
|
name:UIKeyboardDidHideNotification
|
||||||
|
object:nil];
|
||||||
|
#endif
|
||||||
|
|
||||||
inputDelegate = nil;
|
inputDelegate = nil;
|
||||||
inputView = [[TextInputHandler alloc] initWithKeyboard:self];
|
inputView = [[TextInputHandler alloc] initWithKeyboard:self];
|
||||||
inputView.delegate = self;
|
inputView.delegate = self;
|
||||||
inputView.clearsOnBeginEditing = YES;
|
inputView.clearsOnBeginEditing = YES;
|
||||||
[inputView layoutIfNeeded];
|
[inputView layoutIfNeeded];
|
||||||
|
_keyboardVisible = NO;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,9 @@ uint getSizeNextPOT(uint size);
|
||||||
NSLock *_eventLock;
|
NSLock *_eventLock;
|
||||||
SoftKeyboard *_keyboardView;
|
SoftKeyboard *_keyboardView;
|
||||||
Common::List<GameController*> _controllers;
|
Common::List<GameController*> _controllers;
|
||||||
|
#if TARGET_OS_IOS
|
||||||
|
UIInterfaceOrientation _currentOrientation;
|
||||||
|
#endif
|
||||||
UIBackgroundTaskIdentifier _backgroundSaveStateTask;
|
UIBackgroundTaskIdentifier _backgroundSaveStateTask;
|
||||||
|
|
||||||
EAGLContext *_mainContext;
|
EAGLContext *_mainContext;
|
||||||
|
|
|
@ -290,6 +290,9 @@ bool iOS7_fetchEvent(InternalEvent *event) {
|
||||||
self = [super initWithFrame: frame];
|
self = [super initWithFrame: frame];
|
||||||
|
|
||||||
_backgroundSaveStateTask = UIBackgroundTaskInvalid;
|
_backgroundSaveStateTask = UIBackgroundTaskInvalid;
|
||||||
|
#if TARGET_OS_IOS
|
||||||
|
_currentOrientation = UIInterfaceOrientationUnknown;
|
||||||
|
#endif
|
||||||
|
|
||||||
[self setupGestureRecognizers];
|
[self setupGestureRecognizers];
|
||||||
|
|
||||||
|
@ -325,7 +328,12 @@ bool iOS7_fetchEvent(InternalEvent *event) {
|
||||||
[_keyboardView setInputDelegate:self];
|
[_keyboardView setInputDelegate:self];
|
||||||
[self addSubview:[_keyboardView inputView]];
|
[self addSubview:[_keyboardView inputView]];
|
||||||
[self addSubview: _keyboardView];
|
[self addSubview: _keyboardView];
|
||||||
[self showKeyboard];
|
if ([self getScreenHeight] > [self getScreenWidth]) {
|
||||||
|
// This will make sure the keyboard is shown in portrait
|
||||||
|
// mode on start of the application since the orientation
|
||||||
|
// handling is performed before the view finish its setup
|
||||||
|
[self showKeyboard];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[self adjustViewFrameForSafeArea];
|
[self adjustViewFrameForSafeArea];
|
||||||
|
@ -344,7 +352,7 @@ bool iOS7_fetchEvent(InternalEvent *event) {
|
||||||
#ifdef __IPHONE_11_0
|
#ifdef __IPHONE_11_0
|
||||||
if ( @available(iOS 11, tvOS 11, *) ) {
|
if ( @available(iOS 11, tvOS 11, *) ) {
|
||||||
CGRect screenSize = [[UIScreen mainScreen] bounds];
|
CGRect screenSize = [[UIScreen mainScreen] bounds];
|
||||||
CGRect newFrame = screenSize;
|
CGRect newFrame = self.frame;
|
||||||
#if TARGET_OS_IOS
|
#if TARGET_OS_IOS
|
||||||
UIEdgeInsets inset = [[[UIApplication sharedApplication] keyWindow] safeAreaInsets];
|
UIEdgeInsets inset = [[[UIApplication sharedApplication] keyWindow] safeAreaInsets];
|
||||||
UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown;
|
UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown;
|
||||||
|
@ -353,14 +361,32 @@ bool iOS7_fetchEvent(InternalEvent *event) {
|
||||||
} else {
|
} else {
|
||||||
orientation = [[UIApplication sharedApplication] statusBarOrientation];
|
orientation = [[UIApplication sharedApplication] statusBarOrientation];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The code below adjust the screen size according to what Apple calls
|
||||||
|
// the "safe area". It also cover the cases when the software keyboard
|
||||||
|
// is visible and has changed the frame height so the keyboard doesn't
|
||||||
|
// cover any part of the game screen.
|
||||||
|
if (orientation != _currentOrientation) {
|
||||||
|
// If the orientation is changed the keyboard will hide or show
|
||||||
|
// depending on the current orientation. The frame size must be
|
||||||
|
// "reset" again to "full" screen size dimension. The keyboard
|
||||||
|
// will then calculate the approriate height when becoming visible.
|
||||||
|
newFrame = screenSize;
|
||||||
|
_currentOrientation = orientation;
|
||||||
|
}
|
||||||
|
// Make sure the frame height (either full screen or resized due to
|
||||||
|
// visible keyboard) is within the safe area.
|
||||||
|
CGFloat safeAreaHeight = screenSize.size.height - inset.top;
|
||||||
|
CGFloat height = newFrame.size.height < safeAreaHeight ? newFrame.size.height : safeAreaHeight;
|
||||||
|
|
||||||
if ( orientation == UIInterfaceOrientationPortrait ) {
|
if ( orientation == UIInterfaceOrientationPortrait ) {
|
||||||
newFrame = CGRectMake(screenSize.origin.x, screenSize.origin.y + inset.top, screenSize.size.width, screenSize.size.height - inset.top);
|
newFrame = CGRectMake(screenSize.origin.x, screenSize.origin.y + inset.top, screenSize.size.width, height);
|
||||||
} else if ( orientation == UIInterfaceOrientationPortraitUpsideDown ) {
|
} else if ( orientation == UIInterfaceOrientationPortraitUpsideDown ) {
|
||||||
newFrame = CGRectMake(screenSize.origin.x, screenSize.origin.y, screenSize.size.width, screenSize.size.height - inset.top);
|
newFrame = CGRectMake(screenSize.origin.x, screenSize.origin.y, screenSize.size.width, height);
|
||||||
} else if ( orientation == UIInterfaceOrientationLandscapeLeft ) {
|
} else if ( orientation == UIInterfaceOrientationLandscapeLeft ) {
|
||||||
newFrame = CGRectMake(screenSize.origin.x, screenSize.origin.y, screenSize.size.width - inset.right, screenSize.size.height);
|
newFrame = CGRectMake(screenSize.origin.x, screenSize.origin.y, screenSize.size.width - inset.right, height);
|
||||||
} else if ( orientation == UIInterfaceOrientationLandscapeRight ) {
|
} else if ( orientation == UIInterfaceOrientationLandscapeRight ) {
|
||||||
newFrame = CGRectMake(screenSize.origin.x + inset.left, screenSize.origin.y, screenSize.size.width - inset.left, screenSize.size.height);
|
newFrame = CGRectMake(screenSize.origin.x + inset.left, screenSize.origin.y, screenSize.size.width - inset.left, height);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
self.frame = newFrame;
|
self.frame = newFrame;
|
||||||
|
@ -371,6 +397,8 @@ bool iOS7_fetchEvent(InternalEvent *event) {
|
||||||
#ifdef __IPHONE_11_0
|
#ifdef __IPHONE_11_0
|
||||||
// This delegate method is called when the safe area of the view changes
|
// This delegate method is called when the safe area of the view changes
|
||||||
-(void)safeAreaInsetsDidChange {
|
-(void)safeAreaInsetsDidChange {
|
||||||
|
[super safeAreaInsetsDidChange];
|
||||||
|
|
||||||
[self adjustViewFrameForSafeArea];
|
[self adjustViewFrameForSafeArea];
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -606,6 +634,10 @@ bool iOS7_fetchEvent(InternalEvent *event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)applicationSuspend {
|
- (void)applicationSuspend {
|
||||||
|
// Make sure to hide the keyboard when suspended. Else the frame
|
||||||
|
// sizing might become incorrect because the NotificationCenter
|
||||||
|
// sends keyboard notifications on resume.
|
||||||
|
[self hideKeyboard];
|
||||||
[self addEvent:InternalEvent(kInputApplicationSuspended, 0, 0)];
|
[self addEvent:InternalEvent(kInputApplicationSuspended, 0, 0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue