Skip to content

Instantly share code, notes, and snippets.

@gavinmn
Last active August 21, 2024 13:03
Show Gist options
  • Save gavinmn/f38cef1a48ba9533716b80708c09920c to your computer and use it in GitHub Desktop.
Save gavinmn/f38cef1a48ba9533716b80708c09920c to your computer and use it in GitHub Desktop.
import SwiftUI
import UIKit
struct ContentView: View {
@State private var offset: CGFloat = 0
@State private var keyboardOpened: Bool = false
var body: some View {
VStack(spacing: 0) {
ScrollView {
ForEach(0..<20) { index in
HStack {
Text("Item \(index + 1)")
.padding()
Spacer()
}
}
}
.scrollDismissesKeyboard(.interactively)
.scrollClipDisabled()
.defaultScrollAnchor(.bottom)
.padding(.bottom, -offset)
TextInputView()
.offset(y: offset)
}
.background {
KeyboardAttachedView(
offset: $offset,
onKeyboardDidShow: {
keyboardOpened = true
},
onKeyboardDidHide: {
keyboardOpened = false
}
)
.frame(height: 0)
.frame(maxHeight: .infinity, alignment: .bottom)
}
.animation(.spring(duration: 0.3), value: keyboardOpened ? nil : offset)
.ignoresSafeArea(.keyboard)
}
}
struct TextInputView: View {
@State private var textFieldText = ""
var body: some View {
TextField("Type here", text: $textFieldText)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
}
struct KeyboardAttachedView: UIViewControllerRepresentable {
var offset: Binding<CGFloat>
var onKeyboardDidShow: (() -> Void)?
var onKeyboardDidHide: (() -> Void)?
func makeUIViewController(context: Context) -> KeyboardObservingViewController {
let viewController = KeyboardObservingViewController(offset: offset)
viewController.onKeyboardDidShow = onKeyboardDidShow
viewController.onKeyboardDidHide = onKeyboardDidHide
return viewController
}
func updateUIViewController(_ uiViewController: KeyboardObservingViewController, context: Context) {}
}
class KeyboardObservingViewController: UIViewController {
var offset: Binding<CGFloat>
var onKeyboardDidShow: (() -> Void)?
var onKeyboardDidHide: (() -> Void)?
var emptyView: UIView = UIView()
init(offset: Binding<CGFloat>) {
self.offset = offset
super.init(nibName: nil, bundle: nil)
setupKeyboardNotifications()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
view.addSubview(emptyView)
emptyView.translatesAutoresizingMaskIntoConstraints = false
emptyView.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor).isActive = true
emptyView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
emptyView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
offset.wrappedValue = emptyView.layer.position.y
}
private func setupKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow), name: UIResponder.keyboardDidShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidHide), name: UIResponder.keyboardDidHideNotification, object: nil)
}
@objc private func keyboardDidShow(notification: Notification) {
onKeyboardDidShow?()
}
@objc private func keyboardDidHide(notification: Notification) {
onKeyboardDidHide?()
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment