Last active
August 21, 2024 13:03
-
-
Save gavinmn/f38cef1a48ba9533716b80708c09920c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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