Kode diperbarui untuk Xcode, beta 7.
Anda tidak perlu padding, ScrollView atau List untuk mencapai ini. Meskipun solusi ini akan menyenangkan mereka juga. Saya memasukkan dua contoh di sini.
Yang pertama memindahkan semua textField ke atas, jika keyboard muncul untuk salah satu dari mereka. Tetapi hanya jika diperlukan. Jika keyboard tidak menyembunyikan bidang teks, mereka tidak akan bergerak.
Pada contoh kedua, tampilan hanya bergerak cukup untuk menghindari persembunyian bidang teks aktif.
Kedua contoh menggunakan kode umum yang sama yang ditemukan di bagian akhir: GeometryGetter dan KeyboardGuardian
Contoh Pertama (tampilkan semua bidang teks)
struct ContentView: View {
@ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 1)
@State private var name = Array<String>.init(repeating: "", count: 3)
var body: some View {
VStack {
Group {
Text("Some filler text").font(.largeTitle)
Text("Some filler text").font(.largeTitle)
}
TextField("enter text #1", text: $name[0])
.textFieldStyle(RoundedBorderTextFieldStyle())
TextField("enter text #2", text: $name[1])
.textFieldStyle(RoundedBorderTextFieldStyle())
TextField("enter text #3", text: $name[2])
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(GeometryGetter(rect: $kGuardian.rects[0]))
}.offset(y: kGuardian.slide).animation(.easeInOut(duration: 1.0))
}
}
Contoh Kedua (hanya menampilkan bidang aktif)
struct ContentView: View {
@ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 3)
@State private var name = Array<String>.init(repeating: "", count: 3)
var body: some View {
VStack {
Group {
Text("Some filler text").font(.largeTitle)
Text("Some filler text").font(.largeTitle)
}
TextField("text #1", text: $name[0], onEditingChanged: { if $0 { self.kGuardian.showField = 0 } })
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(GeometryGetter(rect: $kGuardian.rects[0]))
TextField("text #2", text: $name[1], onEditingChanged: { if $0 { self.kGuardian.showField = 1 } })
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(GeometryGetter(rect: $kGuardian.rects[1]))
TextField("text #3", text: $name[2], onEditingChanged: { if $0 { self.kGuardian.showField = 2 } })
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(GeometryGetter(rect: $kGuardian.rects[2]))
}.offset(y: kGuardian.slide).animation(.easeInOut(duration: 1.0))
}.onAppear { self.kGuardian.addObserver() }
.onDisappear { self.kGuardian.removeObserver() }
}
GeometryGetter
Ini adalah tampilan yang menyerap ukuran dan posisi tampilan induknya. Untuk mencapainya, ini dipanggil di dalam pengubah .background. Ini adalah pengubah yang sangat kuat, bukan hanya cara untuk menghias latar belakang tampilan. Saat meneruskan tampilan ke .background (MyView ()), MyView mendapatkan tampilan yang dimodifikasi sebagai induk. Penggunaan GeometryReader memungkinkan tampilan mengetahui geometri induknya.
Misalnya: Text("hello").background(GeometryGetter(rect: $bounds))
akan mengisi batas variabel, dengan ukuran dan posisi tampilan Teks, dan menggunakan ruang koordinat global.
struct GeometryGetter: View {
@Binding var rect: CGRect
var body: some View {
GeometryReader { geometry in
Group { () -> AnyView in
DispatchQueue.main.async {
self.rect = geometry.frame(in: .global)
}
return AnyView(Color.clear)
}
}
}
}
Pembaruan Saya menambahkan DispatchQueue.main.async, untuk menghindari kemungkinan mengubah status tampilan saat sedang dirender. ***
Penjaga Keyboard
Tujuan KeyboardGuardian, adalah untuk melacak acara keyboard / menyembunyikan acara dan menghitung berapa banyak ruang yang perlu digeser tampilan.
Pembaruan: Saya memodifikasi KeyboardGuardian untuk menyegarkan slide, ketika pengguna tab dari satu bidang ke bidang lainnya
import SwiftUI
import Combine
final class KeyboardGuardian: ObservableObject {
public var rects: Array<CGRect>
public var keyboardRect: CGRect = CGRect()
public var keyboardIsHidden = true
@Published var slide: CGFloat = 0
var showField: Int = 0 {
didSet {
updateSlide()
}
}
init(textFieldCount: Int) {
self.rects = Array<CGRect>(repeating: CGRect(), count: textFieldCount)
}
func addObserver() {
NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyBoardDidHide(notification:)), name: UIResponder.keyboardDidHideNotification, object: nil)
}
func removeObserver() {
NotificationCenter.default.removeObserver(self)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
@objc func keyBoardWillShow(notification: Notification) {
if keyboardIsHidden {
keyboardIsHidden = false
if let rect = notification.userInfo?["UIKeyboardFrameEndUserInfoKey"] as? CGRect {
keyboardRect = rect
updateSlide()
}
}
}
@objc func keyBoardDidHide(notification: Notification) {
keyboardIsHidden = true
updateSlide()
}
func updateSlide() {
if keyboardIsHidden {
slide = 0
} else {
let tfRect = self.rects[self.showField]
let diff = keyboardRect.minY - tfRect.maxY
if diff > 0 {
slide += diff
} else {
slide += min(diff, 0)
}
}
}
}