Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[iOS] [iPad] fix avoid bottom inset in split view mode #35535

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 13 additions & 39 deletions shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,6 @@ - (void)setupNotificationCenterObservers {
name:UIKeyboardWillChangeFrameNotification
object:nil];

[center addObserver:self
selector:@selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification
object:nil];

[center addObserver:self
selector:@selector(onAccessibilityStatusChanged:)
name:UIAccessibilityVoiceOverStatusChanged
Expand Down Expand Up @@ -1190,12 +1185,19 @@ - (void)updateViewportPadding {

#pragma mark - Keyboard events

// using keyboardWillChangeFrame instead of keyboardWill(Show/Hide) because of split/floating
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says "keybaordWill(show/hide), but we don't have a "keyboardWillShow", maybe just "keyboardWillBeHidden"?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keyboardWillShow/keyboardWillHide are part of the UIKit framework; so keyboardWillBeHidden is not appropriated here and my comment is still relevant 🙂
BTW I tried to answer to you in my opened issue last week but I could not without closing the issue.
(nothing related but I loved the document you wrote related to blurs... so interesting to read !)

// keyboard on iPad
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capital letter at the beginning and period at the end. Also maybe link to the issue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix that this week 👍

- (void)keyboardWillChangeFrame:(NSNotification*)notification {
CGRect screenRect = [[UIScreen mainScreen] bounds];
NSDictionary* info = [notification userInfo];
CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

// Ignore keyboard notifications related to other apps.
// Ignore keyboard notifications related to other apps if not dismissing.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if you show the keyboard in the other app, then tap the Flutter app's text field?

I haven't tried it, just curious.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works normally,

  1. the keyboard appear on the other app and the other app is resized
  2. the textfield in the flutter app becomes the first responder so the flutter app is resized at that moment.

I can make a video if necessary

bool isDismissing =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe:

bool isShowing = CGRectIntersection(keyboardFrame, screenRect);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I'll fix that this week 👍

CGRectEqualToRect(keyboardFrame, CGRectZero) ||
keyboardFrame.origin.y >= screenRect.size.height; // CGRectZero for floating keyboard on iPad
id isLocal = info[UIKeyboardIsLocalUserInfoKey];
if (isLocal && ![isLocal boolValue]) {
if (!isDismissing && isLocal && ![isLocal boolValue]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested If the keyboard is brought be by the other app, does the Flutter app's bottom inset adjust? This logic here seems to prevent Flutter app's bottom inset to be adjusted to the correct value in that case. Which is what I was saying in: flutter/flutter#109845 (comment)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I wrote to @justinmc, if the keyboard is brought by an other app:

  1. the keyboard appear on the other app and the other app is resized
  2. (if you tap in the textfield on the flutter app) the textfield in the flutter app becomes the first responder so the flutter app is resized at that moment.

This is the correct behavior since natives apps have that one.

return;
}

Expand All @@ -1204,13 +1206,6 @@ - (void)keyboardWillChangeFrame:(NSNotification*)notification {
return;
}

CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect screenRect = [[UIScreen mainScreen] bounds];

// Get the animation duration
NSTimeInterval duration =
[[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];

// Considering the iPad's split keyboard, Flutter needs to check if the keyboard frame is present
// in the screen to see if the keyboard is visible.
if (CGRectIntersectsRect(keyboardFrame, screenRect)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line can be changed to if (isShowing) { if you take my suggestion above.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix that this week 👍

Expand All @@ -1223,32 +1218,11 @@ - (void)keyboardWillChangeFrame:(NSNotification*)notification {
} else {
self.targetViewInsetBottom = 0;
}
[self startKeyBoardAnimation:duration];
}

- (void)keyboardWillBeHidden:(NSNotification*)notification {
NSDictionary* info = [notification userInfo];

// Ignore keyboard notifications related to other apps.
id isLocal = info[UIKeyboardIsLocalUserInfoKey];
if (isLocal && ![isLocal boolValue]) {
return;
}

// Ignore keyboard notifications if engine’s viewController is not current viewController.
if ([_engine.get() viewController] != self) {
return;
}

if (self.targetViewInsetBottom != 0) {
// Ensure the keyboard will be dismissed. Just like the keyboardWillChangeFrame,
// keyboardWillBeHidden is also in an animation block in iOS sdk, so we don't need to set the
// animation curve. Related issue: https://github.com/flutter/flutter/issues/99951
self.targetViewInsetBottom = 0;
NSTimeInterval duration =
[[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[self startKeyBoardAnimation:duration];
}
// Get the animation duration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Period at the end (I know you didn't write this comment, but it's a good chance to fix it! 😁).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix that this week 👍

NSTimeInterval duration =
[[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[self startKeyBoardAnimation:duration];
}

- (void)startKeyBoardAnimation:(NSTimeInterval)duration {
Expand Down