Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save cyrilzakka/d214998aa16cda7d3a870fcf9444fd85 to your computer and use it in GitHub Desktop.
Save cyrilzakka/d214998aa16cda7d3a870fcf9444fd85 to your computer and use it in GitHub Desktop.
A wrapper view with an input that sticks to the top of the keyboard during interactive dismissal.
import SwiftUI
import UIKit
struct TextInputView: View {
@State private var textFieldText = ""
var body: some View {
TextField("Type here", text: $textFieldText)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
}
struct KeyboardAnchoredContainer<Content: View>: UIViewControllerRepresentable {
typealias ViewController = KeyboardAnchoredContainerViewController
@ViewBuilder var content: () -> Content
func makeUIViewController(context: Context) -> ViewController<Content> {
ViewController(content: content())
}
func updateUIViewController(_ uiViewController: ViewController<Content>, context: Context) {}
}
class KeyboardAnchoredContainerViewController<Content: View>: UIViewController {
var content: Content
init(content: Content) {
self.content = content
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
setUpContent()
setUpInput()
}
private func setUpContent() {
let hostingView = UIHostingController(rootView: content)
hostingView.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(hostingView.view)
// Anchor content to top of keyboard layout
NSLayoutConstraint.activate([
hostingView.view.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor),
hostingView.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
hostingView.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
hostingView.view.topAnchor.constraint(equalTo: view.topAnchor),
])
}
private func setUpInput() {
let textInputView = TextInputView()
let hostingView = UIHostingController(rootView: textInputView)
view.addSubview(hostingView.view)
hostingView.view.translatesAutoresizingMaskIntoConstraints = false
// Anchor input to top of keyboard layout
NSLayoutConstraint.activate([
hostingView.view.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor),
hostingView.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
hostingView.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
hostingView.view.heightAnchor.constraint(equalToConstant: 46)
])
}
}
struct ContentView: View {
var body: some View {
KeyboardAnchoredContainer {
VStack {
ScrollView {
ForEach(0..<20) { index in
HStack {
Text("Item \(index + 1)")
.padding()
Spacer()
}
}
}
.scrollDismissesKeyboard(.interactively)
.scrollClipDisabled()
.defaultScrollAnchor(.bottom)
// Inset content slightly to avoid overlap with text input.
.contentMargins([.bottom], EdgeInsets(top: 0, leading: 0, bottom: 40, trailing: 0))
}
}
// This is key, otherwise the text input taps into SwiftUI's keyboard safe area avoidance.
.ignoresSafeArea()
}
}
#Preview {
ContentView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment