LeoNatan / LNPopupUI

A SwiftUI library for presenting views as popups, much like the Apple Music and Podcasts apps.
MIT License
320 stars 29 forks source link

popup bar and popup content can get stuck in wrong position when dismissing a modal sheet with the keyboard in a transitory state #11

Closed svenoaks closed 2 years ago

svenoaks commented 2 years ago

This probably needs to be done on LNPopupController, but I'm posting it here for now since I'm using LNPopupUI.

If the keyboard comes out, the position of the popup bar will move to be on top of the keyboard. Is there a way to prevent this behavior so the popup bar stays fixed behind the keyboard. It seems the moving can cause certain bugs where the bar gets stuck in the wrong position as in the attached video. It can get stuck in even worse positions where opening the popup will not fix it.

Even if you need to keep this behavior, maybe there is some easy way to prevent it from within LNPopupController, so that I can make the change just for my case?

https://user-images.githubusercontent.com/5206058/132543998-c1a87255-5ebf-4d24-a8ba-e376e842350a.mp4

LeoNatan commented 2 years ago

Hmm, wow, never seen that. Need to check if this is a SwiftUI layout bug, or my underlying framework is buggy. Will look soon.

LeoNatan commented 2 years ago

BTW, this isn’t supposed to happen. The popup bar is supposed to remain glued to the tab bar. What OS is this?

svenoaks commented 2 years ago

It's iOS 14.7.1. I'll see if it's something I'm doing in the app to make it unglued.

Here's another video that's a worse case where it gets stuck completely.

https://user-images.githubusercontent.com/5206058/132551541-689dea70-9553-4203-bfd4-7020c3a5e573.mp4

svenoaks commented 2 years ago

Ok it seems .popupBarCustomView is what causes the bar to be unglued from the tab bar, if I don't use it, it stays glued. However, the worse bug still happens. I'll see if I can make a small test project where it can be reproduced.

LeoNatan commented 2 years ago

Yes, please. That would be helpful.

svenoaks commented 2 years ago

Hi, test project here can repo it easily: https://github.com/svenoaks/testPopupUI

Easier to use two hands.

For demo1.mp4 I click on the TextField then rapidly swipe down the sheet to dismiss with other hand. For demo2.mp4 I click on the TextField then jam the CLOSE button with other hand.

It's only happening when the keyboard is in some kind of transitory state.

If I get rid of .popupBarCustomView, I can't seem to make demo1 happen, but demo2 still does.

https://user-images.githubusercontent.com/5206058/132588433-1c90510e-9050-468b-b4b7-b114817afa65.mp4

https://user-images.githubusercontent.com/5206058/132588451-e0fd165c-21cf-47f8-9206-d1e22fec9bbb.mp4

LeoNatan commented 2 years ago

This seems like a bug in SwiftUI. It is hellbent on providing the incorrect layout size for the custom popup bar view. There is no issue that I could see with a standard bar (because it is all UIKit). I've implemented a nasty hack that basically ignores popup bar size changes when the keyboard is open, and when it gets closed, it updates the size in case an actual size update is necessary. I think this is a relatively safe hack, as the custom popup bar view would normally change size only after user interaction.

LeoNatan commented 2 years ago

Please try 1.2.17. Thanks

svenoaks commented 2 years ago

The one from demo1.mp4 is fixed in 1.2.17 but demo2 still happens. You have to use the Close button. It can happen with or without the custom popup bar. I updated the demo project without the custom bar, and here's a video of it happening without the custom popup bar.

https://user-images.githubusercontent.com/5206058/132714846-f4b11818-2ba0-4ff3-b41a-a625f033bb4c.mp4

LeoNatan commented 2 years ago

Interesting. Will look into it again.

LeoNatan commented 2 years ago

Interesting, it doesn't reproduce on iOS 15. That's good. I will see what hacks are necessary on iOS 14 to resolve this.

LeoNatan commented 2 years ago

Sorry, unable to reproduce.

https://user-images.githubusercontent.com/2270433/132773698-88e1df42-9658-4d8f-b839-7a5797c2f452.mp4

https://user-images.githubusercontent.com/2270433/132773717-6e87e9d1-e4f2-48cc-af42-c10494ed00a2.mp4

svenoaks commented 2 years ago

I tap on the TextFIeld with one hand, then immediately tap Close with the other hand, there's gotta be very little delay.

LeoNatan commented 2 years ago

Not reproducing:

https://user-images.githubusercontent.com/2270433/132774955-78cdb510-3b25-4514-9779-e55724b21498.MP4

Please find a consistent way to reproduce this issue.

svenoaks commented 2 years ago

try latest commit, it automates causing it two seconds after tapping on DOCUMENT PICKER button. I tried it on the iPhone 12 Pro Max Simulator iOS 14.5 (make sure the soft keyboard is coming out in Messages first, it's I/O...Keyboard...Toggle Soft Keyboard, but I think you have to click this when a TextField is focused), and iPhone 6s with iOS 14.7.1.

Not happening on iPad Pro, but SwiftUI uses a different component for .sheet there, so that might have something to do with it.

Not just an academic problem, I caused this a few times accidentally before I figured out where it was coming from.

LeoNatan commented 2 years ago

Your commit reproduced the issue, thanks. Investigating.

LeoNatan commented 2 years ago

You know, I'm not even sure this is a bug with the popup. It seems like resigning the first responder too quickly breaks the internal layout handling for safe areas. Even without any popup, notice how your tab view looks:

Simulator Screen Shot - iPhone 12 Pro Max - 2021-09-10 at 03 31 01

On iOS 15, this is fixed:

Simulator Screen Shot - iPhone 12 Pro Max - 2021-09-10 at 03 33 37

Likewise, your issue doesn't occur there:

https://user-images.githubusercontent.com/2270433/132779968-52b35cc1-561c-4db5-af37-3792e877ebf1.mp4

I'm not sure what more I can do here. It seems SwiftUI layout is broken on iOS 14.

svenoaks commented 2 years ago

Good find, it's a SwiftUI bug and glad to see fixed in iOS15.

LeoNatan commented 2 years ago
Screen Shot 2021-09-10 at 05 55 36

So, if I do this hack, it fixes the issues. 🙈 The problem comes from bad state management inside SwiftUI, where if the keyboard will hide notification is sent too early (~5 runloop spins), SwiftUI layout breaks.

Unfortunately, I don't feel comfortable adding this hack into LNPopupUI. If you'd like to add it, you can just register some object to listen to UIApplication.keyboardWillHideNotification and repost the notification again, after 5 runloop spins. You can maybe only run it if iOS 14 and below.

svenoaks commented 2 years ago

Thanks for that