-
-
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
edgesIgnoringSafeArea
to always be set to.bottom
. I think a better solution is to set it to.bottom
only when the keyboard is shown.Solution
Change
.edgesIgnoringSafeArea(.bottom)
to.edgesIgnoringSafeArea(currentHeight == 0 ? [] : .bottom)
.