Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AvoidSoftInputView breaks with screen swipe #54

Closed
1 of 2 tasks
nucleartux opened this issue Apr 1, 2022 · 24 comments · Fixed by #56 or #60
Closed
1 of 2 tasks

AvoidSoftInputView breaks with screen swipe #54

nucleartux opened this issue Apr 1, 2022 · 24 comments · Fixed by #56 or #60
Assignees
Labels
bug Something isn't working

Comments

@nucleartux
Copy link
Contributor

nucleartux commented Apr 1, 2022

Environment

Library version: 2.4.1
OS version: iPhone 13, iOS 15.4
"@react-navigation/native": "^6.0.8",
"@react-navigation/native-stack": "^6.6.1",

Affected platforms

  • Android
  • iOS

Current behavior

Simulator.Screen.Recording.-.iPhone.13.-.2022-04-01.at.20.47.27.mp4

Expected behavior

Keyboard avoid view

Reproduction

  1. Open screen
  2. Focus keyboard
  3. Start swiping back
  4. Cancel swipe
    Simplified code:
 <View style={styles.container}>
      <AvoidSoftInputView style={styles.container} avoidOffset={16}>
        <ImageBackground
          source={require("./bg.png")}
          imageStyle={styles.bgImage}
          resizeMode="repeat"
          style={styles.bg}
        >
          <FlatList />
        </ImageBackground>
        <View style={styles.bottom}>
          <TextInput />
        </View>
      </AvoidSoftInputView>
      <SafeAreaView style={styles.footer} edges={["bottom"]} />
    </View>

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  ...
})

And I have related question: now, when I swipe screen keyboard still present but keyboard avoid view smoothly disappears (and content scrolls down). How I can prevent this behavior?

@nucleartux nucleartux added the bug Something isn't working label Apr 1, 2022
@a-eid
Copy link

a-eid commented Apr 1, 2022

I think you should not use AvoidSoftInputView, could you try setEnable(true) when the screen is active & setEanble(false) when it's not ?

@nucleartux
Copy link
Contributor Author

nucleartux commented Apr 1, 2022

I deleted AvoidSoftInputView and added this code:

  const onFocusEffect = useCallback(() => {
    AvoidSoftInput.setEnabled(true); 
    return () => {
      AvoidSoftInput.setEnabled(false);
    };
  }, []);

  useFocusEffect(onFocusEffect);

Now it completely hides navigation header. How I can fix that?

Simulator.Screen.Recording.-.iPhone.13.-.2022-04-01.at.21.13.04.mp4

And anyway bug still present. Even if I try to disable on transition start:

  useEffect(() => {
    const unsubscribe = navigation.addListener("transitionStart", () => {
      AvoidSoftInput.setEnabled(false);
    });

    return unsubscribe;
  }, [navigation]);

@mateusz1913 mateusz1913 self-assigned this Apr 2, 2022
@mateusz1913
Copy link
Owner

Hi @nucleartux

And I have related question: now, when I swipe screen keyboard still present but keyboard avoid view smoothly disappears (and content scrolls down). How I can prevent this behavior?

That's default iOS behavior, keyboard is still fully visible, even though app receives events from UIResponder.keyboardWillChangeFrameNotification:

  • first when swipe is started - from: "keyboardCoordinatesWhenItIsVisible", to: "keyboardCoordinatesWhenItIsDismissed"
  • second when swipe is aborted - from: "keyboardCoordinatesWhenItIsDismissed", to: "keyboardCoordinatesWhenItIsVisible").

I don't know if it can be prevented, you can try with writing your own logic for applying padding to your container based on useSoftInputShown & useSoftInputHidden hooks, but I don't guarantee, that it will make a satisfying result.

@mateusz1913
Copy link
Owner

mateusz1913 commented Apr 2, 2022

@nucleartux issue is fixed and available in version 2.4.2 🎉

@nucleartux
Copy link
Contributor Author

@mateusz1913 hello
I updated library and added screenOptions. Now screen (avoid soft input part) moves together with keyboard but still breaks on swipe cancel. So it's expected behaviour, right?

Simulator.Screen.Recording.-.iPhone.13.-.2022-04-03.at.11.55.37.mov

@mateusz1913
Copy link
Owner

On the provided video, after you cancel swipe, can you scroll to the very bottom (so the input and all messages are visible as it was before the swipe)?

If not, please create reproduction repo and link it in issue's description, so I will be able to investigate and reopen this issue

@nucleartux
Copy link
Contributor Author

It's not possible.

Simulator.Screen.Recording.-.iPhone.13.-.2022-04-03.at.12.19.28.mov

I will try to make repro demo.

@nucleartux
Copy link
Contributor Author

I'm in process of repro but found another bug(?). Can you help me investigate it?
https://github.com/nucleartux/TestProject

Simulator.Screen.Recording.-.iPhone.13.-.2022-04-03.at.12.58.32.mp4

@nucleartux
Copy link
Contributor Author

nucleartux commented Apr 3, 2022

Ok I get it. If you add avoidOffset={16} to my demo it will collapse keyboard after swipe cancel.
Without avoidOffset it adds height every time swipe canceled.

So maybe it's two separate bugs, i'm not sure.

@mateusz1913
Copy link
Owner

I downloaded your repo and can't reproduce behavior from video

Simulator.Screen.Recording.-.iPhone.13.-.2022-04-03.at.14.35.28.mp4

I added nice Swift package ShowTime, so you will see where I make touches

@nucleartux
Copy link
Contributor Author

How it is even possible? I tried delete node_modules and pods and reinstall everything again but bug still exists.

@mateusz1913
Copy link
Owner

mateusz1913 commented Apr 3, 2022

Which simulator, do you use?

@nucleartux
Copy link
Contributor Author

Simulator. iPhone 13, iOS 15.4

@mateusz1913
Copy link
Owner

Weird. I will try on real device

@nucleartux
Copy link
Contributor Author

I tried on real device (iPhone X, latest iOS) - same bug.

@mateusz1913
Copy link
Owner

Ok, I reproduced on real device. Will investigate it and probably create separate ticket.

In the meantime, please prepare that chat repro and I will reopen this ticket

@mateusz1913
Copy link
Owner

I will track second one here: #59

@nucleartux
Copy link
Contributor Author

I made separate branch for this issue https://github.com/nucleartux/TestProject/tree/issue-54

Simulator.Screen.Recording.-.iPhone.13.-.2022-04-03.at.17.47.27.mp4

@mateusz1913 mateusz1913 reopened this Apr 3, 2022
@mateusz1913
Copy link
Owner

@nucleartux Can you try following patch?

diff --git a/node_modules/react-native-avoid-softinput/ios/AvoidSoftInputManager.swift b/node_modules/react-native-avoid-softinput/ios/AvoidSoftInputManager.swift
index 3f30b5a..685dd3f 100644
--- a/node_modules/react-native-avoid-softinput/ios/AvoidSoftInputManager.swift
+++ b/node_modules/react-native-avoid-softinput/ios/AvoidSoftInputManager.swift
@@ -34,6 +34,8 @@ class AvoidSoftInputManager: NSObject {
     private var showDelay: Double = SHOW_ANIMATION_DELAY_IN_SECONDS
     private var showDuration: Double = SHOW_ANIMATION_DURATION_IN_SECONDS
     private var softInputVisible: Bool = false
+    private var wasAddOffsetInRootViewAborted = false
+    private var wasAddOffsetInScrollViewAborted = false
     
     func setAvoidOffset(_ offset: NSNumber) {
         avoidOffset = CGFloat(offset.floatValue)
@@ -157,7 +159,7 @@ class AvoidSoftInputManager: NSObject {
     
     private func decreaseOffsetInRootView(from: CGFloat, to: CGFloat, rootView: UIView) {
         let addedOffset = to - from
-        let newBottomOffset = isShowAnimationRunning ? bottomOffset : bottomOffset + addedOffset
+        let newBottomOffset = isShowAnimationRunning || wasAddOffsetInRootViewAborted ? bottomOffset : bottomOffset + addedOffset
         
         if newBottomOffset < 0 {
             return
@@ -175,7 +177,7 @@ class AvoidSoftInputManager: NSObject {
     
     private func increaseOffsetInRootView(from: CGFloat, to: CGFloat, rootView: UIView) {
         let addedOffset = to - from
-        let newBottomOffset = isHideAnimationRunning ? bottomOffset : bottomOffset + addedOffset
+        let newBottomOffset = isHideAnimationRunning || wasAddOffsetInRootViewAborted ? bottomOffset : bottomOffset + addedOffset
         
         if newBottomOffset < 0 {
             return
@@ -211,6 +213,7 @@ class AvoidSoftInputManager: NSObject {
     
     private func addOffsetInRootView(_ offset: CGFloat, firstResponder: UIView, rootView: UIView) {
         guard let firstResponderPosition = firstResponder.superview?.convert(firstResponder.frame.origin, to: nil) else {
+            wasAddOffsetInRootViewAborted = true
             return
         }
         
@@ -221,13 +224,15 @@ class AvoidSoftInputManager: NSObject {
         
         let firstResponderDistanceToBottom = UIScreen.main.bounds.size.height - (firstResponderPosition.y + firstResponder.frame.height) - bottomSafeInset
         
-        let newOffset = max(offset - firstResponderDistanceToBottom, 0) + avoidOffset
+        let newOffset = max(offset - firstResponderDistanceToBottom, 0)
 
         if (newOffset <= 0) {
+            wasAddOffsetInRootViewAborted = true
             return
         }
         
-        bottomOffset = newOffset
+        wasAddOffsetInRootViewAborted = false
+        bottomOffset = newOffset + avoidOffset
         
         beginShowAnimation(initialOffset: 0, addedOffset: bottomOffset)
         UIView.animate(withDuration: showDuration, delay: showDelay, options: [.beginFromCurrentState, easingOption]) {
@@ -265,7 +270,7 @@ class AvoidSoftInputManager: NSObject {
     
     private func decreaseOffsetInScrollView(from: CGFloat, to: CGFloat, firstResponder: UIView, scrollView: UIScrollView, rootView: UIView) {
         let addedOffset = to - from
-        let newBottomOffset = isShowAnimationRunning ? bottomOffset : bottomOffset + addedOffset
+        let newBottomOffset = isShowAnimationRunning || wasAddOffsetInScrollViewAborted ? bottomOffset : bottomOffset + addedOffset
         let scrollToOffset = getScrollToOffset(softInputHeight: to, firstResponder: firstResponder, scrollView: scrollView, rootView: rootView)
         
         if newBottomOffset < 0 {
@@ -291,7 +296,7 @@ class AvoidSoftInputManager: NSObject {
     
     private func increaseOffsetInScrollView(from: CGFloat, to: CGFloat, firstResponder: UIView, scrollView: UIScrollView, rootView: UIView) {
         let addedOffset = to - from
-        let newBottomOffset = isHideAnimationRunning ? bottomOffset : bottomOffset + addedOffset
+        let newBottomOffset = isHideAnimationRunning || wasAddOffsetInScrollViewAborted ? bottomOffset : bottomOffset + addedOffset
         let scrollToOffset = getScrollToOffset(softInputHeight: to, firstResponder: firstResponder, scrollView: scrollView, rootView: rootView)
         
         if newBottomOffset < 0 {
@@ -344,6 +349,7 @@ class AvoidSoftInputManager: NSObject {
         }
         
         guard let scrollViewPosition = scrollView.superview?.convert(scrollView.frame.origin, to: nil) else {
+            wasAddOffsetInScrollViewAborted = true
             return
         }
         
@@ -351,13 +357,15 @@ class AvoidSoftInputManager: NSObject {
         
         let scrollToOffset = getScrollToOffset(softInputHeight: offset, firstResponder: firstResponder, scrollView: scrollView, rootView: rootView)
         
-        let newOffset = max(offset - scrollViewDistanceToBottom, 0) + avoidOffset
+        let newOffset = max(offset - scrollViewDistanceToBottom, 0)
         
         if newOffset <= 0 {
+            wasAddOffsetInScrollViewAborted = true
             return
         }
         
-        bottomOffset = newOffset
+        wasAddOffsetInScrollViewAborted = false
+        bottomOffset = newOffset + avoidOffset
         
         if !softInputVisible {
             // Save original scroll insets

I think it should resolve both issues

@nucleartux
Copy link
Contributor Author

Tested on repro and real project, works fine!
You saved my life 🕺

@nucleartux
Copy link
Contributor Author

nucleartux commented Apr 3, 2022

But one question. It works fine with fullScreenGestureEnabled enabled but without it on swipe flat list goes under keyboard (video in very first post). Can I somehow workaround it and with disabled fullScreenGestureEnabled?

It's just question, issue is solved anyway.

@mateusz1913
Copy link
Owner

I'll check that, probably it shouldn't work like that

@mateusz1913
Copy link
Owner

Ah, ok, I misunderstood your question. So I guess that fullScreenGestureEnabled option is making keyboard interactive when dismissing it

@mateusz1913
Copy link
Owner

Fix is available in v2.4.3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
3 participants