-
-
Save scottmatthewman/722987c9ad40f852e2b6a185f390f88d to your computer and use it in GitHub Desktop.
| import SwiftUI | |
| import Combine | |
| struct AdaptsToSoftwareKeyboard: ViewModifier { | |
| @State var currentHeight: CGFloat = 0 | |
| func body(content: Content) -> some View { | |
| content | |
| .padding(.bottom, currentHeight) | |
| .edgesIgnoringSafeArea(.bottom) | |
| .onAppear(perform: subscribeToKeyboardEvents) | |
| } | |
| private func subscribeToKeyboardEvents() { | |
| NotificationCenter.Publisher( | |
| center: NotificationCenter.default, | |
| name: UIResponder.keyboardWillShowNotification | |
| ).compactMap { notification in | |
| notification.userInfo?["UIKeyboardFrameEndUserInfoKey"] as? CGRect | |
| }.map { rect in | |
| rect.height | |
| }.subscribe(Subscribers.Assign(object: self, keyPath: \.currentHeight)) | |
| NotificationCenter.Publisher( | |
| center: NotificationCenter.default, | |
| name: UIResponder.keyboardWillHideNotification | |
| ).compactMap { notification in | |
| CGFloat.zero | |
| }.subscribe(Subscribers.Assign(object: self, keyPath: \.currentHeight)) | |
| } | |
| } |
| import SwiftUI | |
| struct KeyboardAwareScrollableView : View { | |
| @ObjectBinding var venue: SomeDataModel | |
| var body: some View { | |
| // Apply to any view with an instrinsic scroll view – ScrollView, Form, List, etc | |
| Form { | |
| Section { | |
| TextField($venue.name, placeholder: Text("Venue Name")) | |
| } | |
| Section(header: Text("Address")) { | |
| TextField($venue.street, placeholder: Text("Street")) | |
| TextField($venue.city, placeholder: Text("City")) | |
| TextField($venue.country, placeholder: Text("Country")) | |
| TextField($venue.postalCode, placeholder: Text("Postcode/ZIP")) | |
| } | |
| // etc. | |
| } | |
| .navigationBarTitle(Text("New venue")) | |
| .modifier(AdaptsToSoftwareKeyboard()) // <-- apply the modifier here | |
| } | |
| } |
This seems to have resolved my issue. Thank you for putting this together 👍 .
This works perfectly!
You could also add .animation(.default) after .padding(.bottom, currentHeight) to smooth out the transition since it's pretty fast and it's somewhat noticeable.
@jaimeealmanza to go a step further, you can use the following animation to match the keyboard transition more closely:
extension Animation {
static var keyboard: Animation {
.interpolatingSpring(mass: 3, stiffness: 1000, damping: 500, initialVelocity: 0.0)
}
}
Did you notice that this code stopped working with Xcode 12 beta 3 on iOS 14 beta 3? At least for me, it works great on beta 2 and earlier. The notification process seems to work fine but the view doesn't seem to respect the padding. Can't quite figure out why that is. Thanks!
@ntornado see this tweet.
@ntornado see this tweet.
Thanks, @damirstuhec!
It doesn't work for me unfortunately. I only got ScrollView with 10 textfields. I think scrollview don't respect padding in Xcode 12.5.1
Nice :)
Will this work for TextEditor?
@ethanyuwang did you find a solution for your issue?

Great! I've made two changes that some might take as improvements:
1. Extension on View for a cleaner API
Usage
2. Edges
Most people probably don't want the
edgesIgnoringSafeAreato always be set to.bottom. I think a better solution is to set it to.bottomonly when the keyboard is shown.Solution
Change
.edgesIgnoringSafeArea(.bottom)to.edgesIgnoringSafeArea(currentHeight == 0 ? [] : .bottom).