react-native-universal-keyboard-aware-scrollview 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +387 -0
- package/android/app/build.gradle +182 -0
- package/android/app/debug.keystore +0 -0
- package/android/app/proguard-rules.pro +14 -0
- package/android/app/src/debug/AndroidManifest.xml +7 -0
- package/android/app/src/debugOptimized/AndroidManifest.xml +7 -0
- package/android/app/src/main/AndroidManifest.xml +25 -0
- package/android/app/src/main/java/com/anonymous/reactnativeuniversalkeyboardawarescrollview/MainActivity.kt +61 -0
- package/android/app/src/main/java/com/anonymous/reactnativeuniversalkeyboardawarescrollview/MainApplication.kt +56 -0
- package/android/app/src/main/res/drawable/ic_launcher_background.xml +6 -0
- package/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png +0 -0
- package/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png +0 -0
- package/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png +0 -0
- package/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png +0 -0
- package/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png +0 -0
- package/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
- package/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
- package/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- package/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp +0 -0
- package/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- package/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- package/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp +0 -0
- package/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- package/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- package/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp +0 -0
- package/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- package/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- package/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp +0 -0
- package/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- package/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- package/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp +0 -0
- package/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- package/android/app/src/main/res/values/colors.xml +6 -0
- package/android/app/src/main/res/values/strings.xml +5 -0
- package/android/app/src/main/res/values/styles.xml +11 -0
- package/android/app/src/main/res/values-night/colors.xml +1 -0
- package/android/build.gradle +89 -0
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/android/gradle.properties +65 -0
- package/android/gradlew +251 -0
- package/android/gradlew.bat +94 -0
- package/android/settings.gradle +39 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/universalkeyboard/UniversalKeyboardModule.kt +349 -0
- package/android/src/main/java/com/universalkeyboard/UniversalKeyboardPackage.kt +21 -0
- package/ios/.xcode.env +11 -0
- package/ios/Podfile +60 -0
- package/ios/Podfile.lock +2001 -0
- package/ios/Podfile.properties.json +5 -0
- package/ios/UniversalKeyboard.h +24 -0
- package/ios/UniversalKeyboard.m +413 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/AppDelegate.swift +70 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png +0 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/AppIcon.appiconset/Contents.json +14 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/Contents.json +6 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenBackground.colorset/Contents.json +20 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenLegacy.imageset/Contents.json +23 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenLegacy.imageset/image.png +0 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenLegacy.imageset/image@2x.png +0 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Images.xcassets/SplashScreenLegacy.imageset/image@3x.png +0 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Info.plist +76 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/PrivacyInfo.xcprivacy +48 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/SplashScreen.storyboard +48 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/Supporting/Expo.plist +12 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/reactnativeuniversalkeyboardawarescrollview-Bridging-Header.h +3 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview/reactnativeuniversalkeyboardawarescrollview.entitlements +5 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview.xcodeproj/project.pbxproj +540 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview.xcodeproj/xcshareddata/xcschemes/reactnativeuniversalkeyboardawarescrollview.xcscheme +88 -0
- package/ios/reactnativeuniversalkeyboardawarescrollview.xcworkspace/contents.xcworkspacedata +10 -0
- package/package.json +61 -0
- package/react-native-universal-keyboard-aware-scrollview.podspec +32 -0
- package/react-native.config.js +18 -0
- package/src/NativeModule.ts +61 -0
- package/src/components/KeyboardAwareScrollView.tsx +388 -0
- package/src/components/index.ts +5 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useKeyboard.ts +360 -0
- package/src/index.ts +27 -0
- package/src/types.ts +87 -0
- package/src/utils/KeyboardController.ts +112 -0
- package/src/utils/index.ts +1 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#import <React/RCTBridgeModule.h>
|
|
2
|
+
#import <React/RCTEventEmitter.h>
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* UniversalKeyboard - Native iOS module for detecting keyboard height changes.
|
|
6
|
+
*
|
|
7
|
+
* This module provides reliable keyboard detection that works in:
|
|
8
|
+
* - Normal React Native screens
|
|
9
|
+
* - React Native Modal components
|
|
10
|
+
* - BottomSheet components
|
|
11
|
+
* - Any overlay/presentation scenarios
|
|
12
|
+
*
|
|
13
|
+
* It uses UIKeyboardWillChangeFrameNotification which is the most reliable
|
|
14
|
+
* notification for keyboard changes on iOS, providing accurate frame
|
|
15
|
+
* information including during interactive dismissals and keyboard size
|
|
16
|
+
* changes.
|
|
17
|
+
*/
|
|
18
|
+
@interface UniversalKeyboard : RCTEventEmitter <RCTBridgeModule>
|
|
19
|
+
|
|
20
|
+
@property(nonatomic, assign) BOOL hasListeners;
|
|
21
|
+
@property(nonatomic, assign) CGFloat lastKeyboardHeight;
|
|
22
|
+
@property(nonatomic, assign) BOOL isKeyboardVisible;
|
|
23
|
+
|
|
24
|
+
@end
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
#import "UniversalKeyboard.h"
|
|
2
|
+
#import <React/RCTLog.h>
|
|
3
|
+
#import <React/RCTUtils.h>
|
|
4
|
+
#import <UIKit/UIKit.h>
|
|
5
|
+
|
|
6
|
+
@implementation UniversalKeyboard
|
|
7
|
+
|
|
8
|
+
RCT_EXPORT_MODULE();
|
|
9
|
+
|
|
10
|
+
#pragma mark - Initialization
|
|
11
|
+
|
|
12
|
+
- (instancetype)init {
|
|
13
|
+
self = [super init];
|
|
14
|
+
if (self) {
|
|
15
|
+
_hasListeners = NO;
|
|
16
|
+
_lastKeyboardHeight = 0;
|
|
17
|
+
_isKeyboardVisible = NO;
|
|
18
|
+
}
|
|
19
|
+
return self;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
- (void)dealloc {
|
|
23
|
+
[self removeKeyboardObservers];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#pragma mark - RCTEventEmitter
|
|
27
|
+
|
|
28
|
+
+ (BOOL)requiresMainQueueSetup {
|
|
29
|
+
return YES;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
- (NSArray<NSString *> *)supportedEvents {
|
|
33
|
+
return @[
|
|
34
|
+
@"keyboardDidShow", @"keyboardDidHide", @"keyboardHeightChanged",
|
|
35
|
+
@"keyboardWillShow", @"keyboardWillHide"
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
- (void)startObserving {
|
|
40
|
+
_hasListeners = YES;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
- (void)stopObserving {
|
|
44
|
+
_hasListeners = NO;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#pragma mark - Public Methods
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Start listening for keyboard events.
|
|
51
|
+
* This method MUST be called from JS before keyboard events will be emitted.
|
|
52
|
+
*/
|
|
53
|
+
RCT_EXPORT_METHOD(startListening : (RCTPromiseResolveBlock)
|
|
54
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
55
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
56
|
+
@try {
|
|
57
|
+
[self setupKeyboardObservers];
|
|
58
|
+
resolve(@YES);
|
|
59
|
+
} @catch (NSException *exception) {
|
|
60
|
+
reject(
|
|
61
|
+
@"ERROR",
|
|
62
|
+
[NSString stringWithFormat:@"Failed to start keyboard listener: %@",
|
|
63
|
+
exception.reason],
|
|
64
|
+
nil);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Stop listening for keyboard events.
|
|
71
|
+
* Call this when the component unmounts to prevent issues.
|
|
72
|
+
*/
|
|
73
|
+
RCT_EXPORT_METHOD(stopListening : (RCTPromiseResolveBlock)
|
|
74
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
75
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
76
|
+
@try {
|
|
77
|
+
[self removeKeyboardObservers];
|
|
78
|
+
resolve(@YES);
|
|
79
|
+
} @catch (NSException *exception) {
|
|
80
|
+
reject(@"ERROR",
|
|
81
|
+
[NSString stringWithFormat:@"Failed to stop keyboard listener: %@",
|
|
82
|
+
exception.reason],
|
|
83
|
+
nil);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get the current keyboard height.
|
|
90
|
+
*/
|
|
91
|
+
RCT_EXPORT_METHOD(getKeyboardHeight : (RCTPromiseResolveBlock)
|
|
92
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
93
|
+
resolve(@(self.lastKeyboardHeight));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check if the keyboard is currently visible.
|
|
98
|
+
*/
|
|
99
|
+
RCT_EXPORT_METHOD(isKeyboardVisible : (RCTPromiseResolveBlock)
|
|
100
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
101
|
+
resolve(@(self.isKeyboardVisible));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Force dismiss the keyboard.
|
|
106
|
+
*/
|
|
107
|
+
RCT_EXPORT_METHOD(dismissKeyboard : (RCTPromiseResolveBlock)
|
|
108
|
+
resolve rejecter : (RCTPromiseRejectBlock)reject) {
|
|
109
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
110
|
+
@try {
|
|
111
|
+
UIWindow *window = [self getKeyWindow];
|
|
112
|
+
if (window) {
|
|
113
|
+
[window endEditing:YES];
|
|
114
|
+
}
|
|
115
|
+
resolve(@YES);
|
|
116
|
+
} @catch (NSException *exception) {
|
|
117
|
+
reject(@"ERROR",
|
|
118
|
+
[NSString stringWithFormat:@"Failed to dismiss keyboard: %@",
|
|
119
|
+
exception.reason],
|
|
120
|
+
nil);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#pragma mark - Keyboard Observers
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Sets up observers for keyboard notifications.
|
|
129
|
+
* Uses multiple notifications for comprehensive keyboard tracking.
|
|
130
|
+
*/
|
|
131
|
+
- (void)setupKeyboardObservers {
|
|
132
|
+
// Remove existing observers first
|
|
133
|
+
[self removeKeyboardObservers];
|
|
134
|
+
|
|
135
|
+
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
|
136
|
+
|
|
137
|
+
// Main notification - provides frame information for all keyboard changes
|
|
138
|
+
[center addObserver:self
|
|
139
|
+
selector:@selector(keyboardWillChangeFrame:)
|
|
140
|
+
name:UIKeyboardWillChangeFrameNotification
|
|
141
|
+
object:nil];
|
|
142
|
+
|
|
143
|
+
// Additional notifications for will show/hide
|
|
144
|
+
[center addObserver:self
|
|
145
|
+
selector:@selector(keyboardWillShow:)
|
|
146
|
+
name:UIKeyboardWillShowNotification
|
|
147
|
+
object:nil];
|
|
148
|
+
|
|
149
|
+
[center addObserver:self
|
|
150
|
+
selector:@selector(keyboardWillHide:)
|
|
151
|
+
name:UIKeyboardWillHideNotification
|
|
152
|
+
object:nil];
|
|
153
|
+
|
|
154
|
+
// Did show/hide for confirmation
|
|
155
|
+
[center addObserver:self
|
|
156
|
+
selector:@selector(keyboardDidShow:)
|
|
157
|
+
name:UIKeyboardDidShowNotification
|
|
158
|
+
object:nil];
|
|
159
|
+
|
|
160
|
+
[center addObserver:self
|
|
161
|
+
selector:@selector(keyboardDidHide:)
|
|
162
|
+
name:UIKeyboardDidHideNotification
|
|
163
|
+
object:nil];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Removes all keyboard observers to prevent memory leaks.
|
|
168
|
+
*/
|
|
169
|
+
- (void)removeKeyboardObservers {
|
|
170
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
#pragma mark - Keyboard Notification Handlers
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Handler for UIKeyboardWillChangeFrameNotification.
|
|
177
|
+
* This is the most reliable notification for keyboard frame changes.
|
|
178
|
+
*/
|
|
179
|
+
- (void)keyboardWillChangeFrame:(NSNotification *)notification {
|
|
180
|
+
if (!self.hasListeners)
|
|
181
|
+
return;
|
|
182
|
+
|
|
183
|
+
NSDictionary *userInfo = notification.userInfo;
|
|
184
|
+
if (!userInfo)
|
|
185
|
+
return;
|
|
186
|
+
|
|
187
|
+
CGRect beginFrame = [userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
|
|
188
|
+
CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
|
189
|
+
NSTimeInterval duration =
|
|
190
|
+
[userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
|
|
191
|
+
UIViewAnimationCurve curve =
|
|
192
|
+
[userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
|
|
193
|
+
|
|
194
|
+
// Calculate keyboard height
|
|
195
|
+
CGFloat keyboardHeight = [self calculateKeyboardHeightFromFrame:endFrame];
|
|
196
|
+
|
|
197
|
+
// Emit height change event
|
|
198
|
+
if (keyboardHeight != self.lastKeyboardHeight) {
|
|
199
|
+
self.lastKeyboardHeight = keyboardHeight;
|
|
200
|
+
|
|
201
|
+
[self sendEventWithName:@"keyboardHeightChanged"
|
|
202
|
+
body:[self createEventDataWithHeight:keyboardHeight
|
|
203
|
+
duration:duration
|
|
204
|
+
curve:curve
|
|
205
|
+
beginFrame:beginFrame
|
|
206
|
+
endFrame:endFrame]];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Handler for UIKeyboardWillShowNotification.
|
|
212
|
+
*/
|
|
213
|
+
- (void)keyboardWillShow:(NSNotification *)notification {
|
|
214
|
+
if (!self.hasListeners)
|
|
215
|
+
return;
|
|
216
|
+
|
|
217
|
+
NSDictionary *userInfo = notification.userInfo;
|
|
218
|
+
CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
|
219
|
+
NSTimeInterval duration =
|
|
220
|
+
[userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
|
|
221
|
+
UIViewAnimationCurve curve =
|
|
222
|
+
[userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
|
|
223
|
+
|
|
224
|
+
CGFloat keyboardHeight = [self calculateKeyboardHeightFromFrame:endFrame];
|
|
225
|
+
|
|
226
|
+
[self sendEventWithName:@"keyboardWillShow"
|
|
227
|
+
body:[self createEventDataWithHeight:keyboardHeight
|
|
228
|
+
duration:duration
|
|
229
|
+
curve:curve
|
|
230
|
+
beginFrame:CGRectZero
|
|
231
|
+
endFrame:endFrame]];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Handler for UIKeyboardWillHideNotification.
|
|
236
|
+
*/
|
|
237
|
+
- (void)keyboardWillHide:(NSNotification *)notification {
|
|
238
|
+
if (!self.hasListeners)
|
|
239
|
+
return;
|
|
240
|
+
|
|
241
|
+
NSDictionary *userInfo = notification.userInfo;
|
|
242
|
+
NSTimeInterval duration =
|
|
243
|
+
[userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
|
|
244
|
+
UIViewAnimationCurve curve =
|
|
245
|
+
[userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
|
|
246
|
+
|
|
247
|
+
[self sendEventWithName:@"keyboardWillHide"
|
|
248
|
+
body:[self createEventDataWithHeight:0
|
|
249
|
+
duration:duration
|
|
250
|
+
curve:curve
|
|
251
|
+
beginFrame:CGRectZero
|
|
252
|
+
endFrame:CGRectZero]];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Handler for UIKeyboardDidShowNotification.
|
|
257
|
+
*/
|
|
258
|
+
- (void)keyboardDidShow:(NSNotification *)notification {
|
|
259
|
+
if (!self.hasListeners)
|
|
260
|
+
return;
|
|
261
|
+
|
|
262
|
+
self.isKeyboardVisible = YES;
|
|
263
|
+
|
|
264
|
+
NSDictionary *userInfo = notification.userInfo;
|
|
265
|
+
CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
|
266
|
+
CGFloat keyboardHeight = [self calculateKeyboardHeightFromFrame:endFrame];
|
|
267
|
+
|
|
268
|
+
self.lastKeyboardHeight = keyboardHeight;
|
|
269
|
+
|
|
270
|
+
[self sendEventWithName:@"keyboardDidShow"
|
|
271
|
+
body:[self createEventDataWithHeight:keyboardHeight
|
|
272
|
+
duration:0
|
|
273
|
+
curve:0
|
|
274
|
+
beginFrame:CGRectZero
|
|
275
|
+
endFrame:endFrame]];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Handler for UIKeyboardDidHideNotification.
|
|
280
|
+
*/
|
|
281
|
+
- (void)keyboardDidHide:(NSNotification *)notification {
|
|
282
|
+
if (!self.hasListeners)
|
|
283
|
+
return;
|
|
284
|
+
|
|
285
|
+
self.isKeyboardVisible = NO;
|
|
286
|
+
self.lastKeyboardHeight = 0;
|
|
287
|
+
|
|
288
|
+
[self sendEventWithName:@"keyboardDidHide"
|
|
289
|
+
body:[self createEventDataWithHeight:0
|
|
290
|
+
duration:0
|
|
291
|
+
curve:0
|
|
292
|
+
beginFrame:CGRectZero
|
|
293
|
+
endFrame:CGRectZero]];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
#pragma mark - Helper Methods
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Calculates the keyboard height from keyboard frame.
|
|
300
|
+
* Accounts for safe area on notch devices.
|
|
301
|
+
*/
|
|
302
|
+
- (CGFloat)calculateKeyboardHeightFromFrame:(CGRect)frame {
|
|
303
|
+
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
|
|
304
|
+
|
|
305
|
+
// If the keyboard frame extends beyond the screen, it's hidden
|
|
306
|
+
if (frame.origin.y >= screenHeight) {
|
|
307
|
+
return 0;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Calculate keyboard height
|
|
311
|
+
CGFloat keyboardHeight = screenHeight - frame.origin.y;
|
|
312
|
+
|
|
313
|
+
// For modals and presented view controllers, we need to check
|
|
314
|
+
// if the keyboard is actually visible
|
|
315
|
+
if (keyboardHeight <= 0) {
|
|
316
|
+
return 0;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return keyboardHeight;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Gets the safe area bottom inset for proper keyboard height calculation.
|
|
324
|
+
*/
|
|
325
|
+
- (CGFloat)getSafeAreaBottomInset {
|
|
326
|
+
UIWindow *window = [self getKeyWindow];
|
|
327
|
+
if (@available(iOS 11.0, *)) {
|
|
328
|
+
if (window) {
|
|
329
|
+
return window.safeAreaInsets.bottom;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return 0;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Gets the key window (works on iOS 13+ and earlier).
|
|
337
|
+
*/
|
|
338
|
+
- (UIWindow *)getKeyWindow {
|
|
339
|
+
if (@available(iOS 13.0, *)) {
|
|
340
|
+
for (UIWindowScene *scene in [UIApplication sharedApplication]
|
|
341
|
+
.connectedScenes) {
|
|
342
|
+
if (scene.activationState == UISceneActivationStateForegroundActive) {
|
|
343
|
+
for (UIWindow *window in scene.windows) {
|
|
344
|
+
if (window.isKeyWindow) {
|
|
345
|
+
return window;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Fallback for iOS < 13
|
|
353
|
+
#pragma clang diagnostic push
|
|
354
|
+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
355
|
+
return [UIApplication sharedApplication].keyWindow;
|
|
356
|
+
#pragma clang diagnostic pop
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Creates the event data dictionary for keyboard events.
|
|
361
|
+
*/
|
|
362
|
+
- (NSDictionary *)createEventDataWithHeight:(CGFloat)height
|
|
363
|
+
duration:(NSTimeInterval)duration
|
|
364
|
+
curve:(UIViewAnimationCurve)curve
|
|
365
|
+
beginFrame:(CGRect)beginFrame
|
|
366
|
+
endFrame:(CGRect)endFrame {
|
|
367
|
+
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
|
|
368
|
+
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
|
|
369
|
+
CGFloat safeAreaBottom = [self getSafeAreaBottomInset];
|
|
370
|
+
|
|
371
|
+
return @{
|
|
372
|
+
@"height" : @(height),
|
|
373
|
+
@"isVisible" : @(height > 0),
|
|
374
|
+
@"duration" : @(duration * 1000), // Convert to milliseconds
|
|
375
|
+
@"easing" : [self easingFromCurve:curve],
|
|
376
|
+
@"screenHeight" : @(screenHeight),
|
|
377
|
+
@"screenWidth" : @(screenWidth),
|
|
378
|
+
@"safeAreaBottom" : @(safeAreaBottom),
|
|
379
|
+
@"endCoordinates" : @{
|
|
380
|
+
@"screenX" : @(endFrame.origin.x),
|
|
381
|
+
@"screenY" : @(endFrame.origin.y),
|
|
382
|
+
@"width" : @(endFrame.size.width),
|
|
383
|
+
@"height" : @(endFrame.size.height)
|
|
384
|
+
},
|
|
385
|
+
@"startCoordinates" : @{
|
|
386
|
+
@"screenX" : @(beginFrame.origin.x),
|
|
387
|
+
@"screenY" : @(beginFrame.origin.y),
|
|
388
|
+
@"width" : @(beginFrame.size.width),
|
|
389
|
+
@"height" : @(beginFrame.size.height)
|
|
390
|
+
},
|
|
391
|
+
@"timestamp" : @([[NSDate date] timeIntervalSince1970] * 1000)
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Converts UIViewAnimationCurve to a string identifier.
|
|
397
|
+
*/
|
|
398
|
+
- (NSString *)easingFromCurve:(UIViewAnimationCurve)curve {
|
|
399
|
+
switch (curve) {
|
|
400
|
+
case UIViewAnimationCurveEaseIn:
|
|
401
|
+
return @"easeIn";
|
|
402
|
+
case UIViewAnimationCurveEaseOut:
|
|
403
|
+
return @"easeOut";
|
|
404
|
+
case UIViewAnimationCurveEaseInOut:
|
|
405
|
+
return @"easeInOut";
|
|
406
|
+
case UIViewAnimationCurveLinear:
|
|
407
|
+
return @"linear";
|
|
408
|
+
default:
|
|
409
|
+
return @"keyboard"; // iOS keyboard uses a custom curve (7)
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
@end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import Expo
|
|
2
|
+
import React
|
|
3
|
+
import ReactAppDependencyProvider
|
|
4
|
+
|
|
5
|
+
@UIApplicationMain
|
|
6
|
+
public class AppDelegate: ExpoAppDelegate {
|
|
7
|
+
var window: UIWindow?
|
|
8
|
+
|
|
9
|
+
var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
|
|
10
|
+
var reactNativeFactory: RCTReactNativeFactory?
|
|
11
|
+
|
|
12
|
+
public override func application(
|
|
13
|
+
_ application: UIApplication,
|
|
14
|
+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
|
15
|
+
) -> Bool {
|
|
16
|
+
let delegate = ReactNativeDelegate()
|
|
17
|
+
let factory = ExpoReactNativeFactory(delegate: delegate)
|
|
18
|
+
delegate.dependencyProvider = RCTAppDependencyProvider()
|
|
19
|
+
|
|
20
|
+
reactNativeDelegate = delegate
|
|
21
|
+
reactNativeFactory = factory
|
|
22
|
+
bindReactNativeFactory(factory)
|
|
23
|
+
|
|
24
|
+
#if os(iOS) || os(tvOS)
|
|
25
|
+
window = UIWindow(frame: UIScreen.main.bounds)
|
|
26
|
+
factory.startReactNative(
|
|
27
|
+
withModuleName: "main",
|
|
28
|
+
in: window,
|
|
29
|
+
launchOptions: launchOptions)
|
|
30
|
+
#endif
|
|
31
|
+
|
|
32
|
+
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Linking API
|
|
36
|
+
public override func application(
|
|
37
|
+
_ app: UIApplication,
|
|
38
|
+
open url: URL,
|
|
39
|
+
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
|
|
40
|
+
) -> Bool {
|
|
41
|
+
return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Universal Links
|
|
45
|
+
public override func application(
|
|
46
|
+
_ application: UIApplication,
|
|
47
|
+
continue userActivity: NSUserActivity,
|
|
48
|
+
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
|
|
49
|
+
) -> Bool {
|
|
50
|
+
let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
|
51
|
+
return super.application(application, continue: userActivity, restorationHandler: restorationHandler) || result
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
|
|
56
|
+
// Extension point for config-plugins
|
|
57
|
+
|
|
58
|
+
override func sourceURL(for bridge: RCTBridge) -> URL? {
|
|
59
|
+
// needed to return the correct URL for expo-dev-client.
|
|
60
|
+
bridge.bundleURL ?? bundleURL()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
override func bundleURL() -> URL? {
|
|
64
|
+
#if DEBUG
|
|
65
|
+
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
|
|
66
|
+
#else
|
|
67
|
+
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
|
68
|
+
#endif
|
|
69
|
+
}
|
|
70
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"colors": [
|
|
3
|
+
{
|
|
4
|
+
"color": {
|
|
5
|
+
"components": {
|
|
6
|
+
"alpha": "1.000",
|
|
7
|
+
"blue": "1.00000000000000",
|
|
8
|
+
"green": "1.00000000000000",
|
|
9
|
+
"red": "1.00000000000000"
|
|
10
|
+
},
|
|
11
|
+
"color-space": "srgb"
|
|
12
|
+
},
|
|
13
|
+
"idiom": "universal"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"info": {
|
|
17
|
+
"version": 1,
|
|
18
|
+
"author": "expo"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images": [
|
|
3
|
+
{
|
|
4
|
+
"idiom": "universal",
|
|
5
|
+
"filename": "image.png",
|
|
6
|
+
"scale": "1x"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"idiom": "universal",
|
|
10
|
+
"filename": "image@2x.png",
|
|
11
|
+
"scale": "2x"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"idiom": "universal",
|
|
15
|
+
"filename": "image@3x.png",
|
|
16
|
+
"scale": "3x"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"info": {
|
|
20
|
+
"version": 1,
|
|
21
|
+
"author": "expo"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>CADisableMinimumFrameDurationOnPhone</key>
|
|
6
|
+
<true/>
|
|
7
|
+
<key>CFBundleDevelopmentRegion</key>
|
|
8
|
+
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
9
|
+
<key>CFBundleDisplayName</key>
|
|
10
|
+
<string>react-native-universal-keyboard-aware-scrollview</string>
|
|
11
|
+
<key>CFBundleExecutable</key>
|
|
12
|
+
<string>$(EXECUTABLE_NAME)</string>
|
|
13
|
+
<key>CFBundleIdentifier</key>
|
|
14
|
+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
15
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
|
16
|
+
<string>6.0</string>
|
|
17
|
+
<key>CFBundleName</key>
|
|
18
|
+
<string>$(PRODUCT_NAME)</string>
|
|
19
|
+
<key>CFBundlePackageType</key>
|
|
20
|
+
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
|
21
|
+
<key>CFBundleShortVersionString</key>
|
|
22
|
+
<string>1.0.0</string>
|
|
23
|
+
<key>CFBundleSignature</key>
|
|
24
|
+
<string>????</string>
|
|
25
|
+
<key>CFBundleURLTypes</key>
|
|
26
|
+
<array>
|
|
27
|
+
<dict>
|
|
28
|
+
<key>CFBundleURLSchemes</key>
|
|
29
|
+
<array>
|
|
30
|
+
<string>com.anonymous.reactnativeuniversalkeyboardawarescrollview</string>
|
|
31
|
+
</array>
|
|
32
|
+
</dict>
|
|
33
|
+
</array>
|
|
34
|
+
<key>CFBundleVersion</key>
|
|
35
|
+
<string>1</string>
|
|
36
|
+
<key>LSMinimumSystemVersion</key>
|
|
37
|
+
<string>12.0</string>
|
|
38
|
+
<key>LSRequiresIPhoneOS</key>
|
|
39
|
+
<true/>
|
|
40
|
+
<key>NSAppTransportSecurity</key>
|
|
41
|
+
<dict>
|
|
42
|
+
<key>NSAllowsArbitraryLoads</key>
|
|
43
|
+
<false/>
|
|
44
|
+
<key>NSAllowsLocalNetworking</key>
|
|
45
|
+
<true/>
|
|
46
|
+
</dict>
|
|
47
|
+
<key>RCTNewArchEnabled</key>
|
|
48
|
+
<true/>
|
|
49
|
+
<key>UILaunchStoryboardName</key>
|
|
50
|
+
<string>SplashScreen</string>
|
|
51
|
+
<key>UIRequiredDeviceCapabilities</key>
|
|
52
|
+
<array>
|
|
53
|
+
<string>arm64</string>
|
|
54
|
+
</array>
|
|
55
|
+
<key>UIRequiresFullScreen</key>
|
|
56
|
+
<false/>
|
|
57
|
+
<key>UIStatusBarStyle</key>
|
|
58
|
+
<string>UIStatusBarStyleDefault</string>
|
|
59
|
+
<key>UISupportedInterfaceOrientations</key>
|
|
60
|
+
<array>
|
|
61
|
+
<string>UIInterfaceOrientationPortrait</string>
|
|
62
|
+
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
|
63
|
+
</array>
|
|
64
|
+
<key>UISupportedInterfaceOrientations~ipad</key>
|
|
65
|
+
<array>
|
|
66
|
+
<string>UIInterfaceOrientationPortrait</string>
|
|
67
|
+
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
|
68
|
+
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
69
|
+
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
70
|
+
</array>
|
|
71
|
+
<key>UIUserInterfaceStyle</key>
|
|
72
|
+
<string>Light</string>
|
|
73
|
+
<key>UIViewControllerBasedStatusBarAppearance</key>
|
|
74
|
+
<false/>
|
|
75
|
+
</dict>
|
|
76
|
+
</plist>
|