Skip to content

Commit

Permalink
refactor(app): further improves keyboard interactions
Browse files Browse the repository at this point in the history
  • Loading branch information
heapwolf committed May 18, 2024
1 parent c28cd63 commit 6b950bc
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 66 deletions.
138 changes: 72 additions & 66 deletions src/app/app.cc
Original file line number Diff line number Diff line change
Expand Up @@ -365,40 +365,15 @@ didFailToContinueUserActivityWithType: (NSString*) userActivityType

- (void) keyboardWillHide: (NSNotification*) notification {
NSDictionary *userInfo = notification.userInfo;
CGRect keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat keyboardHeight = keyboardFrame.size.height;

const auto window = self.app->windowManager.getWindow(0);
window->webview.scrollView.scrollEnabled = YES;

CGRect startFrame = window->webview.frame;
CGRect endFrame = CGRectMake(startFrame.origin.x, startFrame.origin.y, startFrame.size.width, startFrame.size.height + keyboardHeight); // Expanding back

__block CGFloat animationProgress = 0;
NSTimeInterval duration = ([userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]) * 0.9;
NSTimeInterval stepDuration = duration / 120.0;

NSNumber *curve = userInfo[UIKeyboardAnimationCurveUserInfoKey];
UIViewAnimationOptions animationCurve = [curve unsignedIntegerValue] << 16;

[NSTimer scheduledTimerWithTimeInterval:stepDuration repeats:YES block:^(NSTimer * _Nonnull timer) {
animationProgress += stepDuration / duration;

if (animationProgress >= 1.0) {
animationProgress = 1.0;
window->webview.frame = endFrame;
[timer invalidate];
} else {
CGFloat interpolatedProgress = 1 - pow(1 - animationProgress, 3); // Using ease-out cubic is wrong, we could improve this curve

CGRect newFrame = CGRectMake(startFrame.origin.x + (endFrame.origin.x - startFrame.origin.x) * interpolatedProgress,
startFrame.origin.y + (endFrame.origin.y - startFrame.origin.y) * interpolatedProgress,
startFrame.size.width + (endFrame.size.width - startFrame.size.width) * interpolatedProgress,
startFrame.size.height + (endFrame.size.height - startFrame.size.height) * interpolatedProgress);
self.keyboardHeight = keyboardFrame.size.height;
self.animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];

window->webview.frame = newFrame;
}
}];
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(keyboardHide:)];
self.displayLink.preferredFramesPerSecond = 120;
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];

for (const auto window : self.app->windowManager.windows) {
if (window) {
Expand All @@ -407,7 +382,6 @@ didFailToContinueUserActivityWithType: (NSString*) userActivityType
const auto rect = [keyboardFrameBegin CGRectValue];
const auto height = rect.size.height;

window->webview.scrollView.scrollEnabled = YES;
window->bridge.emit("keyboard", JSON::Object::Entries {
{"value", JSON::Object::Entries {
{"event", "will-hide"},
Expand All @@ -430,43 +404,17 @@ didFailToContinueUserActivityWithType: (NSString*) userActivityType
}
}

- (void) keyboardWillShow: (NSNotification*) notification {
- (void)keyboardWillShow: (NSNotification*) notification {
NSDictionary *userInfo = notification.userInfo;
NSValue *keyboardFrameValue = userInfo[UIKeyboardFrameEndUserInfoKey];
CGRect keyboardFrame = [keyboardFrameValue CGRectValue];
CGRect keyboardFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat keyboardHeight = keyboardFrame.size.height;

const auto window = self.app->windowManager.getWindow(0);
window->webview.scrollView.scrollEnabled = NO;

CGRect startFrame = window->webview.frame;
CGRect endFrame = CGRectMake(startFrame.origin.x, startFrame.origin.y, startFrame.size.width, startFrame.size.height - keyboardHeight);
self.keyboardHeight = keyboardFrame.size.height;
self.animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];

__block CGFloat animationProgress = 0;
NSTimeInterval duration = (([userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]) * 1.9);
NSTimeInterval stepDuration = duration / 120.0;

NSNumber *curve = userInfo[UIKeyboardAnimationCurveUserInfoKey];
UIViewAnimationOptions animationCurve = [curve unsignedIntegerValue] << 16;

[NSTimer scheduledTimerWithTimeInterval:stepDuration repeats:YES block:^(NSTimer * _Nonnull timer) {
animationProgress += stepDuration / duration;

if (animationProgress >= 1.0) {
animationProgress = 1.0;
window->webview.frame = endFrame;
[timer invalidate];
} else {
CGFloat interpolatedProgress = 1 - pow(1 - animationProgress, 3);

CGRect newFrame = CGRectMake(startFrame.origin.x + (endFrame.origin.x - startFrame.origin.x) * interpolatedProgress,
startFrame.origin.y + (endFrame.origin.y - startFrame.origin.y) * interpolatedProgress,
startFrame.size.width + (endFrame.size.width - startFrame.size.width) * interpolatedProgress,
startFrame.size.height + (endFrame.size.height - startFrame.size.height) * interpolatedProgress);

window->webview.frame = newFrame;
}
}];
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(keyboardShow:)];
self.displayLink.preferredFramesPerSecond = 60;
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];

for (const auto window : self.app->windowManager.windows) {
if (window && !window->window.isHidden) {
Expand All @@ -475,7 +423,6 @@ didFailToContinueUserActivityWithType: (NSString*) userActivityType
const auto rect = [keyboardFrameBegin CGRectValue];
const auto height = rect.size.height;

window->webview.scrollView.scrollEnabled = NO;
window->bridge.emit("keyboard", JSON::Object::Entries {
{"value", JSON::Object::Entries {
{"event", "will-show"},
Expand All @@ -486,6 +433,65 @@ didFailToContinueUserActivityWithType: (NSString*) userActivityType
}
}

- (void) keyboardShow: (CADisplayLink*) displayLink {
if (self.inMotion) return;
self.inMotion = true;

const auto window = self.app->windowManager.getWindow(0);
const auto stepDuration = (self.animationDuration / 60.0) / 40;

auto timer = [NSTimer scheduledTimerWithTimeInterval:stepDuration repeats:YES block:^(NSTimer * _Nonnull timer) {
CGRect newFrame = window->webview.frame;

CGFloat p = self.progress / self.keyboardHeight;
p = p > 1.0 ? 1.0 : p;
CGFloat easedHeightChange = 0.3 + pow(1.0 - p, 16);
newFrame.size.height -= easedHeightChange;

window->webview.frame = newFrame;

CGFloat progressIncrement = easedHeightChange > 0 ? easedHeightChange : 0;
self.progress += progressIncrement;

if (self.progress >= self.keyboardHeight) {
[displayLink invalidate];
[timer invalidate];
self.inMotion = false;
}
}];

[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

- (void) keyboardHide: (CADisplayLink*) displayLink {
if (self.inMotion) return;
self.inMotion = true;

const auto window = self.app->windowManager.getWindow(0);
const auto stepDuration = self.animationDuration / 60.0;

auto timer = [NSTimer scheduledTimerWithTimeInterval:stepDuration repeats:YES block:^(NSTimer * _Nonnull timer) {
CGRect newFrame = window->webview.frame;

CGFloat p = (self.progress / self.keyboardHeight || 1);
CGFloat easedHeightChange = 8 + pow(--p, 4);
newFrame.size.height += easedHeightChange; // Adjusted to increase the height

window->webview.frame = newFrame;

CGFloat progressIncrement = easedHeightChange > 0 ? easedHeightChange : 0;
self.progress -= progressIncrement; // Decrease the progress

if (self.progress <= 0) { // Check if progress is zero or less
[displayLink invalidate];
[timer invalidate];
self.inMotion = false;
}
}];

[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

- (void) keyboardDidShow: (NSNotification*) notification {
for (const auto window : self.app->windowManager.windows) {
if (window && !window->window.isHidden) {
Expand Down
11 changes: 11 additions & 0 deletions src/app/app.hh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
#include "../ipc/ipc.hh"
#include "../window/window.hh"

#if SSC_PLATFORM_IOS
#import <QuartzCore/QuartzCore.h>
#import <objc/runtime.h>
#endif

namespace SSC {
class App;
}
Expand All @@ -30,6 +35,12 @@ namespace SSC {

#elif SSC_PLATFORM_IOS
UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, assign) CGFloat keyboardHeight;
@property (nonatomic, assign) BOOL inMotion;
@property (nonatomic, assign) CGFloat progress;
@property (nonatomic, assign) NSTimeInterval animationDuration;

- (BOOL) application: (UIApplication*) application
continueUserActivity: (NSUserActivity*) userActivity
restorationHandler: (void (^)(NSArray<id<UIUserActivityRestoring>>*)) restorationHandler;
Expand Down
1 change: 1 addition & 0 deletions src/cli/cli.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4463,6 +4463,7 @@ int main (const int argc, const char* argv[]) {
settings["build_extensions_" + extension + "_ios_compiler_flags"] +
" -framework UniformTypeIdentifiers" +
" -framework CoreBluetooth" +
" -framework QuartzCore" +
" -framework CoreLocation" +
" -framework Network" +
" -framework UserNotifications" +
Expand Down
4 changes: 4 additions & 0 deletions src/cli/templates.hh
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@ constexpr auto gXCodeProject = R"ASCII(// !$*UTF8*$!
2996EDB22770BC1F00C672A2 /* Network.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2996EDB12770BC1F00C672A2 /* Network.framework */; };
2996EDB22770BC1F00C672A3 /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2996EDB12770BC1F00C672A3 /* CoreBluetooth.framework */; };
2996EDB22770BC1F00C672A4 /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2996EDB12770BC1F00C672A4 /* UserNotifications.framework */; };
2996EDB22770BC1F00C672B0 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2996EDB12770BC1F00C672B0 /* QuartzCore.framework */; };
2996EDB22770BC1F00C672A5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 29124C5E2761336B001832A1 /* Assets.xcassets */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -717,6 +718,7 @@ constexpr auto gXCodeProject = R"ASCII(// !$*UTF8*$!
2996EDB12770BC1F00C672A2 /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = System/Library/Frameworks/Network.framework; sourceTree = SDKROOT; };
2996EDB12770BC1F00C672A3 /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; };
2996EDB12770BC1F00C672A4 /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; };
2996EDB12770BC1F00C672B0 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartzcore.framework; path = System/Library/Frameworks/Quartzcore.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -734,6 +736,7 @@ constexpr auto gXCodeProject = R"ASCII(// !$*UTF8*$!
2996EDB22770BC1F00C672A2 /* Network.framework in Frameworks */,
2996EDB22770BC1F00C672A3 /* CoreBluetooth.framework in Frameworks */,
2996EDB22770BC1F00C672A4 /* UserNotifications.framework in Frameworks */,
2996EDB22770BC1F00C672B0 /* QuartzCore.framework in Frameworks */,
294A3CA02768C429007B5B9A /* WebKit.framework in Frameworks */,
290F7EBF2768C49000486988 /* UIKit.framework in Frameworks */,
);
Expand Down Expand Up @@ -807,6 +810,7 @@ constexpr auto gXCodeProject = R"ASCII(// !$*UTF8*$!
2996EDB12770BC1F00C672A2 /* Network.framework */,
2996EDB12770BC1F00C672A3 /* CoreBluetooth.framework */,
2996EDB12770BC1F00C672A4 /* UserNotifications.framework */,
2996EDB12770BC1F00C672B0 /* QuartzCore.framework */,
294A3C7B2763EA7F007B5B9A /* WebKit.framework */,
294A3C792763E9C6007B5B9A /* UIKit.framework */,
);
Expand Down

0 comments on commit 6b950bc

Please sign in to comment.