Commit 5e9e4d2a authored by Javier Ernesto Flores Robles's avatar Javier Ernesto Flores Robles Committed by Commit Bot

[iOS][MF] Keyboard Observer consolidated logic.

To avoid maintaning dupe code, the logic to find the keyboard is
consolidated in the helper. The keyboard view and its best constraining
view can now be accessed as class properties.

Bug: 996669
Change-Id: I4da6883539375cf99a8edf82bbf9b8fec2ba82c6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1765470Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Commit-Queue: Javier Ernesto Flores Robles <javierrobles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#691136}
parent 1a7ff24a
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#import "ios/chrome/browser/autofill/form_suggestion_view.h" #import "ios/chrome/browser/autofill/form_suggestion_view.h"
#import "ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_view.h" #import "ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_view.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h" #import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h"
#import "ios/chrome/browser/ui/util/keyboard_observer_helper.h"
#include "ios/chrome/browser/ui/util/ui_util.h" #include "ios/chrome/browser/ui/util/ui_util.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h" #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h" #import "ios/chrome/common/ui_util/constraints_ui_util.h"
...@@ -80,14 +81,10 @@ CGFloat const kInputAccessoryHeight = 44.0f; ...@@ -80,14 +81,10 @@ CGFloat const kInputAccessoryHeight = 44.0f;
} }
// Returns YES if the keyboard constraint view is present. This view is the one // Returns YES if the keyboard constraint view is present. This view is the one
// used to constraint any presented view. // used to constraint any presented view. iPad always presents in a separate
// popover.
- (BOOL)canPresentView { - (BOOL)canPresentView {
if (IsIPadIdiom()) { return KeyboardObserverHelper.keyboardLayoutGuide || IsIPadIdiom();
// iPad always presents in a separate popover.
return YES;
}
UIView* keyboardView = [self getKeyboardView];
return [self recursiveGetKeyboardConstraintView:keyboardView];
} }
#pragma mark - Public #pragma mark - Public
...@@ -98,13 +95,11 @@ CGFloat const kInputAccessoryHeight = 44.0f; ...@@ -98,13 +95,11 @@ CGFloat const kInputAccessoryHeight = 44.0f;
} }
DCHECK(view); DCHECK(view);
DCHECK(!view.superview); DCHECK(!view.superview);
UIView* keyboardView = [self getKeyboardView]; UIView* keyboardView = KeyboardObserverHelper.keyboardView;
view.accessibilityViewIsModal = YES; view.accessibilityViewIsModal = YES;
[keyboardView.superview addSubview:view]; [keyboardView.superview addSubview:view];
UIView* constrainingView =
[self recursiveGetKeyboardConstraintView:keyboardView];
view.translatesAutoresizingMaskIntoConstraints = NO; view.translatesAutoresizingMaskIntoConstraints = NO;
AddSameConstraints(view, constrainingView); AddSameConstraints(view, KeyboardObserverHelper.keyboardLayoutGuide);
self.keyboardReplacementView = view; self.keyboardReplacementView = view;
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification,
view); view);
...@@ -287,53 +282,6 @@ CGFloat const kInputAccessoryHeight = 44.0f; ...@@ -287,53 +282,6 @@ CGFloat const kInputAccessoryHeight = 44.0f;
[self.inputAccessoryView removeFromSuperview]; [self.inputAccessoryView removeFromSuperview];
} }
// This searches in a keyboard view hierarchy for the best candidate to
// constrain a view to the keyboard.
- (UIView*)recursiveGetKeyboardConstraintView:(UIView*)view {
for (UIView* subview in view.subviews) {
// TODO(crbug.com/845472): verify this on iOS 10-12 and all devices.
// Currently only tested on X-iOS12, 6+-iOS11 and 7+-iOS10. iPhoneX, iOS 11
// and 12 uses "Dock" and iOS 10 uses "Backdrop". iPhone6+, iOS 11 uses
// "Dock".
if ([NSStringFromClass([subview class]) containsString:@"Dock"] ||
[NSStringFromClass([subview class]) containsString:@"Backdrop"]) {
return subview;
}
UIView* found = [self recursiveGetKeyboardConstraintView:subview];
if (found) {
return found;
}
}
return nil;
}
- (UIView*)getKeyboardView {
NSArray* windows = [UIApplication sharedApplication].windows;
NSUInteger expectedMinWindows = IsIPadIdiom() ? 2 : 3;
if (windows.count < expectedMinWindows)
return nil;
UIWindow* window = windows.lastObject;
for (UIView* subview in window.subviews) {
if ([NSStringFromClass([subview class]) rangeOfString:@"PeripheralHost"]
.location != NSNotFound) {
return subview;
}
if ([NSStringFromClass([subview class]) rangeOfString:@"SetContainer"]
.location != NSNotFound) {
for (UIView* subsubview in subview.subviews) {
if ([NSStringFromClass([subsubview class]) rangeOfString:@"SetHost"]
.location != NSNotFound) {
return subsubview;
}
}
}
}
return nil;
}
- (void)addCustomKeyboardViewIfNeeded { - (void)addCustomKeyboardViewIfNeeded {
if (self.isPaused) { if (self.isPaused) {
return; return;
...@@ -352,7 +300,7 @@ CGFloat const kInputAccessoryHeight = 44.0f; ...@@ -352,7 +300,7 @@ CGFloat const kInputAccessoryHeight = 44.0f;
if (self.inputAccessoryView) { if (self.inputAccessoryView) {
if (IsIPadIdiom()) { if (IsIPadIdiom()) {
// On iPad the keyboard view can change so this updates it when needed. // On iPad the keyboard view can change so this updates it when needed.
UIView* keyboardView = [self getKeyboardView]; UIView* keyboardView = KeyboardObserverHelper.keyboardView;
if (!keyboardView) { if (!keyboardView) {
return; return;
} }
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@protocol LayoutGuideProvider;
// Struct to track the current keyboard state. // Struct to track the current keyboard state.
typedef struct { typedef struct {
// Is YES if the keyboard is visible or becoming visible. // Is YES if the keyboard is visible or becoming visible.
...@@ -39,6 +41,17 @@ typedef struct { ...@@ -39,6 +41,17 @@ typedef struct {
// Helper to observe the keyboard and report updates. // Helper to observe the keyboard and report updates.
@interface KeyboardObserverHelper : NSObject @interface KeyboardObserverHelper : NSObject
// Keyboard's UIView based on some known, undocumented classes. |nil| if the
// keyboard is not present or found.
// This can break on any iOS update to keyboard architecture.
@property(class, readonly, nonatomic) UIView* keyboardView;
// Best layout guide for the keyboard including the prediction part of it. |nil|
// if the keyboard is not present or found.
// This can break on any iOS update to keyboard architecture.
@property(class, readonly, nonatomic) id<LayoutGuideProvider>
keyboardLayoutGuide;
// Flag that indicates if the keyboard is on screen. // Flag that indicates if the keyboard is on screen.
// TODO(crbug.com/974226): look into deprecating keyboardOnScreen for // TODO(crbug.com/974226): look into deprecating keyboardOnScreen for
// isKeyboardVisible. // isKeyboardVisible.
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#import "ios/chrome/browser/ui/util/keyboard_observer_helper.h" #import "ios/chrome/browser/ui/util/keyboard_observer_helper.h"
#include "ios/chrome/browser/ui/util/ui_util.h" #include "ios/chrome/browser/ui/util/ui_util.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
...@@ -26,6 +27,8 @@ ...@@ -26,6 +27,8 @@
@implementation KeyboardObserverHelper @implementation KeyboardObserverHelper
#pragma mark - Public
- (instancetype)init { - (instancetype)init {
self = [super init]; self = [super init];
if (self) { if (self) {
...@@ -53,6 +56,39 @@ ...@@ -53,6 +56,39 @@
return self; return self;
} }
+ (UIView*)keyboardView {
NSArray* windows = [UIApplication sharedApplication].windows;
NSUInteger expectedMinWindows = IsIPadIdiom() ? 2 : 3;
if (windows.count < expectedMinWindows)
return nil;
UIWindow* window = windows.lastObject;
for (UIView* subview in window.subviews) {
if ([NSStringFromClass([subview class]) rangeOfString:@"PeripheralHost"]
.location != NSNotFound) {
return subview;
}
if ([NSStringFromClass([subview class]) rangeOfString:@"SetContainer"]
.location != NSNotFound) {
for (UIView* subsubview in subview.subviews) {
if ([NSStringFromClass([subsubview class]) rangeOfString:@"SetHost"]
.location != NSNotFound) {
return subsubview;
}
}
}
}
return nil;
}
+ (id<LayoutGuideProvider>)keyboardLayoutGuide {
return [self keyboardLayoutGuideInHostView:self.keyboardView];
}
#pragma mark - Keyboard Notifications
- (void)keyboardWillDidChangeFrame:(NSNotification*)notification { - (void)keyboardWillDidChangeFrame:(NSNotification*)notification {
[self updateKeyboardState]; [self updateKeyboardState];
} }
...@@ -70,12 +106,34 @@ ...@@ -70,12 +106,34 @@
}); });
} }
#pragma mark - keyboard state detection #pragma mark - Private
// This searches in the passed view hierarchy for the best Layout Guide for the
// keyboard.
+ (id<LayoutGuideProvider>)keyboardLayoutGuideInHostView:(UIView*)hostView {
for (UIView* subview in hostView.subviews) {
// Currently only tested on X-iOS12, 6+-iOS11 and 7+-iOS10. iPhoneX, iOS 11
// and 12 uses "Dock" and iOS 10 uses "Backdrop". iPhone6+, iOS 11 uses
// "Dock".
if ([NSStringFromClass([subview class]) containsString:@"Dock"] ||
[NSStringFromClass([subview class]) containsString:@"Backdrop"]) {
return subview;
}
id<LayoutGuideProvider> found =
[self keyboardLayoutGuideInHostView:subview];
if (found) {
return found;
}
}
return nil;
}
#pragma mark Keyboard State Detection
// Update keyboard state by looking at keyboard frame and the existence of some // Update keyboard state by looking at keyboard frame and the existence of some
// classes to detect split view or pickers. // classes to detect split view or pickers.
- (void)updateKeyboardState { - (void)updateKeyboardState {
UIView* keyboardView = [self keyboardView]; UIView* keyboardView = KeyboardObserverHelper.keyboardView;
CGFloat windowHeight = [UIScreen mainScreen].bounds.size.height; CGFloat windowHeight = [UIScreen mainScreen].bounds.size.height;
CGRect keyboardFrame = keyboardView.frame; CGRect keyboardFrame = keyboardView.frame;
...@@ -98,35 +156,6 @@ ...@@ -98,35 +156,6 @@
} }
} }
// Finds the keyboard UIView based on some known, undocumented classes.
// This can break on any iOS update to keyboard architecture.
- (UIView*)keyboardView {
NSArray* windows = [UIApplication sharedApplication].windows;
NSUInteger expectedMinWindows = IsIPadIdiom() ? 2 : 3;
if (windows.count < expectedMinWindows)
return nil;
UIWindow* window = windows.lastObject;
for (UIView* subview in window.subviews) {
if ([NSStringFromClass([subview class]) rangeOfString:@"PeripheralHost"]
.location != NSNotFound) {
return subview;
}
if ([NSStringFromClass([subview class]) rangeOfString:@"SetContainer"]
.location != NSNotFound) {
for (UIView* subsubview in subview.subviews) {
if ([NSStringFromClass([subsubview class]) rangeOfString:@"SetHost"]
.location != NSNotFound) {
return subsubview;
}
}
}
}
return nil;
}
// Checks for a picker UIView* under the given |view|. // Checks for a picker UIView* under the given |view|.
- (BOOL)containsPickerView:(UIView*)view { - (BOOL)containsPickerView:(UIView*)view {
for (UIView* subview in view.subviews) { for (UIView* subview in view.subviews) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment