-
Notifications
You must be signed in to change notification settings - Fork 37
Re-pad the view on re-render while keyboard continues to show #6
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
base: master
Are you sure you want to change the base?
Conversation
If the view re-renders, and the position of the first responder changes, while the keyboard is showing.
3abff55
to
624a5de
Compare
|
||
|
||
init(rerender: AnyPublisher<Any, Never>? = nil) { | ||
var publishers = [Publishers.keyboardHeight.map { arg -> Any in arg }.eraseToAnyPublisher()] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Futzing with the types is really frustrating here considering I don't even care what the values are when self.rerender
fires. Ideally, I wouldn't have to eraseToAnyPublisher
nor map
this to convert it to <Any, Never>
, and the client wouldn't have to do likewise when passing in their publisher, but I'm not sure how to type things to accomplish that.
private func bottomPadding(forGeometry geometry: GeometryProxy) -> CGFloat { | ||
let keyboardTop = geometry.frame(in: .global).height - Publishers.keyboardHeight.value | ||
var focusedTextInputBottom = UIResponder.currentFirstResponder?.globalFrame?.maxY ?? 0 | ||
focusedTextInputBottom += bottomPadding / 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm dividing by 2 and multiplying by 2 here for reasons described here. My keyboard is around 500pt tall, like I mention in the comments there.
.map { $0.keyboardHeight } | ||
let willHide = NotificationCenter.default.publisher(for: UIApplication.keyboardWillHideNotification) | ||
.map { _ in CGFloat(0) } | ||
keyboardUpdates = Publishers.MergeMany(willShow, willHide).assign(to: \.value, on: subject) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be really slick to convert this publisher into a CurrentValueSubject
so that we didn't have to retain the return value separately, but not sure how to do that.
@@ -9,16 +9,22 @@ | |||
import Combine | |||
import UIKit | |||
|
|||
fileprivate var keyboardUpdates: AnyCancellable? | |||
fileprivate let keyboardHeightPublisher: CurrentValueSubject<CGFloat, Never> = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We change how we're publishing the keyboard height so that KeyboardAdaptive
can read the current value when re-rendering not in response to the keyboard changing, but rather in response to another condition.
I tried caching the keyboard height on the KeyboardAdaptive
modifier e.g. in a @State
property and it didn't work but rather got reset, I would assume when the view hierarchy re-renders.
|
||
private func bottomPadding(forGeometry geometry: GeometryProxy) -> CGFloat { | ||
let keyboardTop = geometry.frame(in: .global).height - Publishers.keyboardHeight.value | ||
var focusedTextInputBottom = UIResponder.currentFirstResponder?.globalFrame?.maxY ?? 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be really nice if we could just observe the frame of this translating, that's the real condition that should prompt us to re-pad, without the client having to do anything. Maybe something using KVO?
The current implementation will only update the padding if the keyboard shows or hides. But what if the view re-renders, moving the first responder up or down, while the keyboard is showing?
This happens in my app, which is a tutorial for setting up a custom keyboard. My view is like this:
Where
TextView
is an instance of this. I won't get into the implementation ofKeyboardState
unless you like, but I have a mechanism where once my custom keyboard opens, it causeskeyboardState.hasBeenOpened
to becometrue
, asynchronously with respect to the keyboard changing.You may notice that the text above the field grows bigger when the keyboard has been opened, so the padding needs to increase. But it doesn't, because the update comes in after the keyboard has already changed.
Here's a PR that's a stab at re-rendering the
ViewModifier
when the keyboard height changes or when a client-provided publisher fires. In my case, that publisher is a binding tokeyboardState.hasBeenOpened
: I change my.keyboardAdaptive()
invocation to look likeThere are a bunch of things I don't like about this approach, but I figured I'd put it up to start a conversation.