Skip to content

Commit db14657

Browse files
authored
Keyboard accessory view safe area api support (#827)
* keyboard-accessory-view now support enable/disable bottom safe area space (iOS only) * remove native default safe-area value, so the default-value will be managed from a single place * removed extra call to update constraint - in iOS native. + pass the useSafeArea flag in the root params object instead of initialProps + fix documentation typo in ActionBar * PR comments - default value is implemented in iOS only + doc change + toggle will be visible also for android devices to show users it won't effect other devices * Change behaviour in keyboard-demo screen, now when the safe-area toggle is changed, the keyboard will re-appear with the right safe-area. applicable only for iOS devices * Changed documentation
1 parent ca01695 commit db14657

File tree

8 files changed

+119
-21
lines changed

8 files changed

+119
-21
lines changed

demo/src/screens/nativeComponentScreens/keyboardInput/KeyboardInputViewScreen.js

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import React, {PureComponent} from 'react';
22
import {ScrollView, StyleSheet, TextInput} from 'react-native';
3-
import {Keyboard, Text, View, Colors, Spacings, Constants, Typography, Button} from 'react-native-ui-lib';
3+
import {Keyboard, Text, View, Colors, Spacings, Constants, Typography, Button, Switch} from 'react-native-ui-lib';
44
const KeyboardAccessoryView = Keyboard.KeyboardAccessoryView;
55
const KeyboardUtils = Keyboard.KeyboardUtils;
6+
import {_} from 'lodash';
67

78
import './demoKeyboards';
89
const KeyboardRegistry = Keyboard.KeyboardRegistry;
@@ -15,7 +16,9 @@ export default class KeyboardInputViewScreen extends PureComponent {
1516
component: undefined,
1617
initialProps: undefined
1718
},
18-
receivedKeyboardData: undefined
19+
receivedKeyboardData: undefined,
20+
useSafeArea: true,
21+
keyboardOpenState: false
1922
};
2023

2124
onKeyboardItemSelected = (keyboardId, params) => {
@@ -49,12 +52,44 @@ export default class KeyboardInputViewScreen extends PureComponent {
4952
return buttons;
5053
}
5154

55+
isCustomKeyboardOpen = () => {
56+
const {keyboardOpenState, customKeyboard} = this.state;
57+
return keyboardOpenState && !_.isEmpty(customKeyboard);
58+
}
59+
5260
resetKeyboardView = () => {
5361
this.setState({customKeyboard: {}});
5462
};
5563

64+
dismissKeyboard = () => {
65+
KeyboardUtils.dismiss();
66+
}
67+
68+
toggleUseSafeArea = () => {
69+
const {useSafeArea} = this.state;
70+
this.setState({useSafeArea: !useSafeArea});
71+
72+
if (this.isCustomKeyboardOpen()) {
73+
this.dismissKeyboard();
74+
this.showLastKeyboard();
75+
}
76+
};
77+
78+
showLastKeyboard() {
79+
const {customKeyboard} = this.state;
80+
this.setState({customKeyboard: {}});
81+
82+
setTimeout(() => {
83+
this.setState({
84+
keyboardOpenState: true,
85+
customKeyboard
86+
});
87+
}, 500);
88+
}
89+
5690
showKeyboardView(component, title) {
5791
this.setState({
92+
keyboardOpenState: true,
5893
customKeyboard: {
5994
component,
6095
initialProps: {title}
@@ -84,7 +119,6 @@ export default class KeyboardInputViewScreen extends PureComponent {
84119
/>
85120
<Button label="Close" link onPress={KeyboardUtils.dismiss} style={styles.button}/>
86121
</View>
87-
88122
<View row>
89123
{this.getToolbarButtons().map((button, index) => (
90124
<Button label={button.text} link onPress={button.onPress} key={index} style={styles.button}/>
@@ -107,10 +141,26 @@ export default class KeyboardInputViewScreen extends PureComponent {
107141
});
108142
};
109143

144+
safeAreaSwitchToggle = () => {
145+
if (!Constants.isIOS) {
146+
return;
147+
}
148+
const {useSafeArea} = this.state;
149+
return (
150+
<View column center>
151+
<View style={styles.separatorLine}/>
152+
<View centerV row margin-10>
153+
<Text text80 dark40>Safe Area Enabled:</Text>
154+
<Switch value={useSafeArea} onValueChange={this.toggleUseSafeArea} marginL-14/>
155+
</View>
156+
<View style={styles.separatorLine}/>
157+
</View>
158+
);
159+
}
160+
110161
render() {
111162
const {message} = this.props;
112-
const {receivedKeyboardData, customKeyboard} = this.state;
113-
163+
const {receivedKeyboardData, customKeyboard, useSafeArea} = this.state;
114164
return (
115165
<View flex bg-dark80>
116166
<ScrollView
@@ -122,6 +172,7 @@ export default class KeyboardInputViewScreen extends PureComponent {
122172
</Text>
123173
<Text testID={'demo-message'}>{receivedKeyboardData}</Text>
124174
<Button label={'Open keyboard #1'} link onPress={this.requestShowKeyboard} style={styles.button}/>
175+
{ this.safeAreaSwitchToggle() }
125176
</ScrollView>
126177

127178
<KeyboardAccessoryView
@@ -135,6 +186,7 @@ export default class KeyboardInputViewScreen extends PureComponent {
135186
onKeyboardResigned={this.onKeyboardResigned}
136187
revealKeyboardInteractive
137188
onRequestShowKeyboard={this.onRequestShowKeyboard}
189+
useSafeArea={useSafeArea}
138190
/>
139191
</View>
140192
);
@@ -149,7 +201,7 @@ const styles = StyleSheet.create({
149201
},
150202
textInput: {
151203
flex: 1,
152-
padding: Spacings.s2,
204+
padding: Spacings.s1,
153205
...Typography.text70
154206
},
155207
button: {
@@ -159,5 +211,10 @@ const styles = StyleSheet.create({
159211
backgroundColor: Colors.white,
160212
borderWidth: 1,
161213
borderColor: Colors.dark60
214+
},
215+
separatorLine: {
216+
flex: 1,
217+
height: 1,
218+
backgroundColor: Colors.dark80
162219
}
163220
});

lib/components/Keyboard/KeyboardInput/CustomKeyboardView.ios.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ export default class CustomKeyboardView extends CustomKeyboardViewBase {
1111
inputRef: PropTypes.object,
1212
initialProps: PropTypes.object,
1313
component: PropTypes.string,
14-
onItemSelected: PropTypes.func
14+
onItemSelected: PropTypes.func,
15+
useSafeArea: PropTypes.bool
16+
};
17+
18+
static defaultProps = {
19+
useSafeArea: true
1520
};
1621

1722
constructor(props) {
@@ -42,14 +47,15 @@ export default class CustomKeyboardView extends CustomKeyboardViewBase {
4247
}
4348

4449
async UNSAFE_componentWillReceiveProps(nextProps) {
45-
const {inputRef: nextInputRef, component: nextComponent, initialProps: nextInitialProps} = nextProps;
50+
const {inputRef: nextInputRef, component: nextComponent, initialProps: nextInitialProps, useSafeArea} = nextProps;
4651
const {component} = this.props;
4752

4853
if (nextInputRef && nextComponent !== component) {
4954
if (nextComponent) {
5055
TextInputKeyboardManager.setInputComponent(nextInputRef, {
5156
component: nextComponent,
52-
initialProps: nextInitialProps
57+
initialProps: nextInitialProps,
58+
useSafeArea
5359
});
5460
} else {
5561
TextInputKeyboardManager.removeInputComponent(nextInputRef);

lib/components/Keyboard/KeyboardInput/KeyboardAccessoryView.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,14 @@ class KeyboardAccessoryView extends Component {
110110
*
111111
* default: false
112112
*/
113-
allowHitsOutsideBounds: PropTypes.bool
113+
allowHitsOutsideBounds: PropTypes.bool,
114+
115+
/**
116+
* iOS only.
117+
* Whether or not to handle SafeArea
118+
* default: true
119+
*/
120+
useSafeArea: PropTypes.bool
114121
};
115122

116123
static iosScrollBehaviors = IOS_SCROLL_BEHAVIORS;
@@ -236,7 +243,8 @@ class KeyboardAccessoryView extends Component {
236243
kbInputRef,
237244
kbComponent,
238245
onItemSelected,
239-
onRequestShowKeyboard
246+
onRequestShowKeyboard,
247+
useSafeArea
240248
} = this.props;
241249

242250
return (
@@ -258,6 +266,7 @@ class KeyboardAccessoryView extends Component {
258266
initialProps={this.processInitialProps()}
259267
onItemSelected={onItemSelected}
260268
onRequestShowKeyboard={onRequestShowKeyboard}
269+
useSafeArea={useSafeArea}
261270
/>
262271
</KeyboardTrackingView>
263272
);

lib/components/Keyboard/KeyboardInput/TextInputKeyboardManager.ios.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import ReactNative, {NativeModules, LayoutAnimation} from 'react-native';
33
const CustomInputControllerTemp = NativeModules.CustomInputControllerTemp;
44

55
export default class TextInputKeyboardManager {
6-
static setInputComponent = (textInputRef, {component, initialProps}) => {
6+
static setInputComponent = (textInputRef, {component, initialProps, useSafeArea}) => {
77
if (!textInputRef || !CustomInputControllerTemp) {
88
return;
99
}
1010
const reactTag = findNodeHandle(textInputRef);
1111
if (reactTag) {
12-
CustomInputControllerTemp.presentCustomInputComponent(reactTag, {component, initialProps});
12+
CustomInputControllerTemp.presentCustomInputComponent(reactTag, {component, initialProps, useSafeArea});
1313
}
1414
};
1515

lib/ios/reactnativeuilib/keyboardinput/rctcustomInputcontroller/RCTCustomInputControllerTemp.m

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ -(UIView*)getFirstResponder:(UIView*)view
114114
return nil;
115115
}
116116

117+
- (BOOL)shouldUseSafeAreaFrom:(NSDictionary *)params {
118+
return [params[@"useSafeArea"] isEqual:@(1)];
119+
}
120+
117121
RCT_EXPORT_METHOD(presentCustomInputComponent:(nonnull NSNumber*)inputFieldTag params:(nonnull NSDictionary*)params)
118122
{
119123
RCTBridge* bridge = [self.bridge valueForKey:@"parentBridge"];
@@ -136,7 +140,8 @@ -(UIView*)getFirstResponder:(UIView*)view
136140

137141
self.customInputComponentPresented = NO;
138142

139-
RCTCustomKeyboardViewController* customKeyboardController = [[RCTCustomKeyboardViewController alloc] init];
143+
BOOL useSafeArea = [self shouldUseSafeAreaFrom:params];
144+
RCTCustomKeyboardViewController* customKeyboardController = [[RCTCustomKeyboardViewController alloc] initWithUsingSafeArea:useSafeArea];
140145
customKeyboardController.rootView = rv;
141146

142147
_WXInputHelperView* helperView = [[_WXInputHelperView alloc] initWithFrame:CGRectZero];

lib/ios/reactnativeuilib/keyboardinput/rctcustomInputcontroller/RCTCustomKeyboardViewController.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
@interface RCTCustomKeyboardViewController : UIInputViewController
1717

1818
- (void) setAllowsSelfSizing:(BOOL)allowsSelfSizing;
19+
- (instancetype)initWithUsingSafeArea:(BOOL)useSafeArea;
1920

2021
@property (nonatomic, strong) NSLayoutConstraint *heightConstraint;
2122
@property (nonatomic, strong) RCTRootView *rootView;

lib/ios/reactnativeuilib/keyboardinput/rctcustomInputcontroller/RCTCustomKeyboardViewController.m

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,26 @@
88
#import "RCTCustomKeyboardViewController.h"
99

1010
#if __has_include(<KeyboardTrackingView/ObservingInputAccessoryViewTemp.h>)
11-
#import <KeyboardTrackingView/ObservingInputAccessoryViewTemp.h>
12-
#define ObservingInputAccessoryViewTemp_IsAvailable true
11+
#import <KeyboardTrackingView/ObservingInputAccessoryViewTemp.h>
12+
#define ObservingInputAccessoryViewTemp_IsAvailable true
1313
#endif
1414

15+
@interface RCTCustomKeyboardViewController ()
16+
@property (nonatomic, assign, getter=isUsingSafeArea) BOOL useSafeArea;
17+
@end
18+
1519
@implementation RCTCustomKeyboardViewController
1620

17-
- (instancetype)init
21+
- (instancetype)initWithUsingSafeArea:(BOOL)useSafeArea
1822
{
1923
self = [super init];
2024

2125
if(self)
2226
{
2327
self.inputView = [[UIInputView alloc] initWithFrame:CGRectZero inputViewStyle:UIInputViewStyleKeyboard];
24-
28+
2529
self.heightConstraint = [self.inputView.heightAnchor constraintEqualToConstant:0];
30+
self.useSafeArea = useSafeArea;
2631

2732
#ifdef ObservingInputAccessoryViewTemp_IsAvailable
2833
ObservingInputAccessoryViewTemp *activeObservingInputAccessoryViewTemp = [ObservingInputAccessoryViewTempManager sharedInstance].activeObservingInputAccessoryViewTemp;
@@ -62,18 +67,33 @@ -(void)setRootView:(RCTRootView*)rootView
6267
_rootView = rootView;
6368
_rootView.translatesAutoresizingMaskIntoConstraints = NO;
6469
[self.inputView addSubview:_rootView];
65-
70+
71+
[self updateRootViewConstraints];
72+
[self.inputView setNeedsLayout];
73+
}
74+
75+
- (void)updateRootViewConstraints {
6676
[_rootView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
6777
[_rootView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
6878
[_rootView.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
79+
80+
NSLayoutYAxisAnchor *yAxisAnchor = [self bottomLayoutAnchorUsingSafeArea:self.isUsingSafeArea];
81+
[_rootView.bottomAnchor constraintEqualToAnchor:yAxisAnchor].active = YES;
82+
}
6983

84+
- (NSLayoutYAxisAnchor *)bottomLayoutAnchorUsingSafeArea:(BOOL)useSafeArea {
7085
NSLayoutYAxisAnchor *yAxisAnchor = self.view.bottomAnchor;
86+
87+
if (!useSafeArea) {
88+
return yAxisAnchor;
89+
}
90+
7191
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3
7292
if (@available(iOS 11.0, *)) {
7393
yAxisAnchor = self.view.safeAreaLayoutGuide.bottomAnchor;
7494
}
7595
#endif
76-
[_rootView.bottomAnchor constraintEqualToAnchor:yAxisAnchor].active = YES;
96+
return yAxisAnchor;
7797
}
7898

7999
@end

src/components/actionBar/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export default class ActionBar extends BaseComponent {
3737
*/
3838
useSafeArea: PropTypes.bool,
3939
/**
40-
* keep the action bar postion relative instead of it absolute position
40+
* keep the action bar position relative instead of it absolute position
4141
*/
4242
keepRelative: PropTypes.bool,
4343
/**

0 commit comments

Comments
 (0)