Skip to content

Conversation

@Bowlerr
Copy link

@Bowlerr Bowlerr commented Apr 3, 2025

📜 Description

Will write up a proper description later and clean up the code.

current issue until done (Android):

  • The multiple line inputs always start the text cursor from the bottom on first focus

    • ideally I want it to start where I press

💡 Motivation and Context

📢 Changelog

JS

iOS

Android

🤔 How Has This Been Tested?

📸 Screenshots (if appropriate):

📝 Checklist

  • CI successfully passed
  • I added new mocks and corresponding unit-tests if library API was changed

@Bowlerr
Copy link
Author

Bowlerr commented Apr 4, 2025

Demo.mp4

@Bowlerr
Copy link
Author

Bowlerr commented Apr 4, 2025

@kirillzyusko lmk what you think

scrollEventThrottle={16}
onContentSizeChange={onContentSizeChange}
onLayout={onScrollViewLayout}
onScroll={scrollHandler}
Copy link
Owner

Choose a reason for hiding this comment

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

It's not very trivial to pass handler in such way. We have to forward that event to external handlers. And it's tricky, because those handlers can be worklet/plain-js handlers. I'll link a chain of PR here in chronological order: #339 #408 #490 #500

Comment on lines +632 to +635
() => scrollY.value,
(current) => {
scrollTo(scrollViewAnimatedRef, 0, current, false);
},
Copy link
Owner

Choose a reason for hiding this comment

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

I think it gives an effect of "keep strong distance between caret and keyboard"

I think default iOS/Android implementation perform scroll only if needed, i. e. here

image

When you move pointer higher:

image

It scrolls (but the expected behavior is that it shouldn't)

Copy link
Author

Choose a reason for hiding this comment

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

I can change this. I liked the effect for inputs that don't fit inside the visible area. Without this the user would need to scroll themselves or beginning to type for the move to happen.

I can change it so the scroll stops at the top of the input instead? and then follows from the caret at bottom until it reaches the bottom of the input where it will sit if it has a maxHeight?

Comment on lines +241 to +272
scrollY.value = spring
? withSpring(goal, { damping: 100, stiffness: 100 }, (finished) => {
log(
"Goal:",
goal,
"Current:",
scrollOffsetY.value,
"EndingPosition:",
scrollY.value,
"Distance From Goal:",
Math.abs(scrollOffsetY.value - goal),
"Finished:",
finished,
);
isScrolling.value = false;
})
: withTiming(goal, undefined, (finished) => {
log(
"Goal:",
goal,
"Current:",
scrollOffsetY.value,
"EndingPosition:",
scrollY.value,
"Distance From Goal:",
Math.abs(scrollOffsetY.value - goal),
"Finished:",
finished,
);
isScrolling.value = false;
});
};
Copy link
Owner

Choose a reason for hiding this comment

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

Why do we need this? We should perform scroll based on a keyboard position? So we should scroll from onMove handlers.

Plain scroll can happen when text input grows, but in this case scrollTo(..., true) with default animation works also pretty well 🤔

Comment on lines 92 to 96
{new Array(10).fill(0).map((_, i) => (
{new Array(5).fill(0).map((_, i) => (
Copy link
Owner

Choose a reason for hiding this comment

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

If you modify code example in such way there is 100% guarantee that e2e tests will fail, because they expect to have certain amount of inputs.

I'd suggest to revert these changes (I understand that it's more convenient not scroll through entire form, but you can keep these changes locally and not commit them to the repository)

Copy link
Author

Choose a reason for hiding this comment

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

Planned to clean this up and wasn't suppose to commit the changes to the example.

type Props = StackScreenProps<ExamplesStackParamList>;
const snapToOffsets = [125, 225, 325, 425, 525, 625];

const BIG_TEXT_TWO = `Where does it come from?
Copy link
Owner

Choose a reason for hiding this comment

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

Maybe let's not repeat code and re-use variables?

But again, for playground code you can keep a local changes and not commit them to the repository.

Copy link
Author

Choose a reason for hiding this comment

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

Planned to clean this up and wasn't suppose to commit the changes to the example.

@kirillzyusko
Copy link
Owner

And feedback based on video:

  • 0:08-0:09 it feels a little bit wrong, that you see scroll and at certain point of time this scroll continues. Do we have selection coordinates before onStart-keyboard event? If not then it can be a bug in keyboard-controller and I think it should be fixed (basically before onStart you should have these coordinates, because we already have focused input)
  • 0:13-0:15 - I think we shouldn't scroll to follow cursor position. If scroll remains in a visible area then better not scroll, since user can still see the input.
  • 0:25-0:27 - feels like scroll is stuttering a little bit?
  • 0:50-0:51 - why we scrolled such a big distance? I thought we always rely on selection coordinates (not on layout anymore)?
  • 0:59-1:00 - animation feels desynchronized?
  • when back scroll turned off it looks like you add back scroll? And when it's enabled I don't see a back scroll?
  • not sure, but since you modified example it looks like snap points are broken now

Overall you did a great job 💪 But certain points must be addressed I don't think I can merge PR in a current state 🤔

But if you don't want to spend too much time on going back-and-forth with review, then you can use that modified version of KeyboardAwareScrollView in your project, since that component uses public API of the lib 🙂

Also if you want we can have a conversation in discord, it should significantly improve our communication process 😊

@Bowlerr
Copy link
Author

Bowlerr commented Apr 4, 2025

I'm going away this weekend so have not much time but would love to jump on discord next week if that works with you @kirillzyusko


0:08-0:09 - feels off

  • Agreed, I believe this is because of the withSpring. Will remove and find another solution to the issue it solved.
    the issue I was solving was: on iOS when you select the last input of the scrolllist, it is off by a few pixels ( if you remove the margin from the text input). My solution is hacky for such an edge case so happy to remove it.

0:13-0:15 - I think we shouldn't scroll to follow cursor position. If scroll remains in a visible area then better not scroll, since user can still see the input.

  • Can change to this, can always add a prop to follow cursor on long inputs?

0:25-0:27 - feels like scroll is stuttering a little bit?

  • need to work on animation timings or could be slow in debug mode on a simulator which i've experienced a lot in the past.

0:50-0:51 - why we scrolled such a big distance? I thought we always rely on selection coordinates (not on layout anymore)?

  • If the input fit within the viewable space in the scrollview -> Scroll input to fill view
  • if it doesn't -> follow selection coordinates

0:59-1:00 - animation feels desynchronized?

  • agree, I'll look into this.

when back scroll turned off it looks like you add back scroll? And when it's enabled I don't see a back scroll?

  • On the example, when toggle is enabled, it disables the back scroll. its enabling the disabling.

not sure, but since you modified example it looks like snap points are broken now

  • How are they supposed to work?
  • It looks correct to me as they are snapping the points to the top of the scroll?

@Bowlerr
Copy link
Author

Bowlerr commented Apr 4, 2025

you can use that modified version of KeyboardAwareScrollView in your project, since that component uses public API of the lib 🙂

Already on it 😎. Thanks for the great api

@kirillzyusko
Copy link
Owner

I'll close this PR in favour of #1026

It'll be published under 1.18.0 hopefully next weel!

kirillzyusko added a commit that referenced this pull request Jul 19, 2025
## 📜 Description

Allow `KeyboardAwareScrollView` to work with inputs that covers whole
screen.

## 💡 Motivation and Context

Previous implementation worked incorrectly when you had a `TextInput`
that gets covered by keyboard and navigation header **simultaneously**.
It was happening because I didn't consider a case, when you have a very
large input - intead I was relying on `TextInput` coordinates on the
screen, and if it overlaps with keyboard, then we need to scroll to
bottom, if it overlaps with header - scroll to top. However in many apps
sometimes it's needed to have a really large input (for example Notes,
Gmail, etc.)

In this PR I re-worked approach and now I don't use `TextInput` layout
coordinates anymore and I rely on selection coordiantes (aka cursor).
The preparation work has been started in
#546
but this PR fully implements new specification (not only one handler).

We can not rely only on selection coordinates, because if input has
`maxHeight` then `selection` coordinates can be very big (if you typed a
lot and text content height exceeds `maxHeight`) - in this case to
handle it properly we clamp the value to `layout.height`, so we still
need to use `useReanimatedFocusedInput` hook.

Closes
#512
#578
#897
#937
#901

## 📢 Changelog

<!-- High level overview of important changes -->
<!-- For example: fixed status bar manipulation; added new types
declarations; -->
<!-- If your changes don't affect one of platform/language below - then
remove this platform/language -->

### Docs

- mention that `bottomOffset` is a distance between keyboard and caret
inside focused `TextInput`;

### E2E

- update assets;

### JS

- added `updateLayoutFromSelection` function;
- don't rely on `input.value` coordinated directly;

## 🤔 How Has This Been Tested?

Tested on iPhone 15 Pro (iOS 17.5) and Pixel 3A (API 33).

Additionally tested via e2e tests.

## 📸 Screenshots (if appropriate):

|iOS|Android|
|---|--------|
|<video
src="https://github.com/user-attachments/assets/744bf413-4aca-49ed-aef8-6bd458d09c19">|<video
src="https://github.com/user-attachments/assets/e09c42c1-9204-4291-9df4-3bf179a0d27a">|

## 📝 Checklist

- [x] CI successfully passed
- [x] I added new mocks and corresponding unit-tests if library API was
changed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants