Skip to content

Instantly share code, notes, and snippets.

@sammcode
Last active August 20, 2024 16:11
Show Gist options
  • Save sammcode/a57b54c9f43154b12c6fb646aca0dc5e to your computer and use it in GitHub Desktop.
Save sammcode/a57b54c9f43154b12c6fb646aca0dc5e to your computer and use it in GitHub Desktop.
import SwiftUI
import SwiftData
import UIKit
@Model
class Item {
var text: String = ""
init(text: String) {
self.text = text
}
}
struct TextInputView: View {
@Environment(\.modelContext) var modelContext
@State private var textFieldText = ""
var body: some View {
TextField("Type here", text: $textFieldText)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.onSubmit {
modelContext.insert(Item(text: textFieldText))
textFieldText = ""
}
}
}
struct KeyboardAnchoredContainer<Content: View, Input: View>: UIViewControllerRepresentable {
typealias ViewController = KeyboardAnchoredContainerViewController
@ViewBuilder var content: () -> Content
@ViewBuilder var input: () -> Input
func makeUIViewController(context: Context) -> ViewController<Content, Input> {
ViewController(content: content(), input: input())
}
func updateUIViewController(_ uiViewController: ViewController<Content, Input>, context: Context) {}
}
class KeyboardAnchoredContainerViewController<Content: View, Input: View>: UIViewController {
var content: Content
var input: Input
init(content: Content, input: Input) {
self.content = content
self.input = input
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 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 {
ItemListView()
} input: {
TextInputView()
}
// This is key, otherwise the text input taps into SwiftUI's keyboard safe area avoidance.
.ignoresSafeArea(.keyboard)
}
}
struct ItemListView: View {
// Define the Query here so the view can update itself.
@Query var items: [Item]
var body: some View {
ScrollView {
VStack {
ForEach(items) { item in
HStack {
Text(item.text)
.padding()
Spacer()
}
}
}
// Inset content slightly to avoid overlap with text input.
.padding(.bottom, 40)
}
.scrollDismissesKeyboard(.interactively)
.scrollClipDisabled()
.defaultScrollAnchor(.bottom)
}
}
#Preview {
ContentView()
.modelContainer(for: Item.self, inMemory: true)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment