Xcode, beta 7 için güncellenen kod.
Bunu başarmak için dolguya, ScrollViews veya Listelere ihtiyacınız yoktur. Her ne kadar bu çözüm onlarla da iyi oynayacak. Buraya iki örnek ekliyorum.
İlki , herhangi biri için klavye görünüyorsa, tüm textField'ı yukarı taşır . Ama sadece gerekirse. Klavye metin alanlarını gizlemezse hareket etmezler.
İkinci örnekte, görünüm yalnızca etkin metin alanını gizlemekten kaçınmak için yeterince hareket eder.
Her iki örnek de, sonunda bulunan aynı ortak kodu kullanır: GeometryGetter ve KeyboardGuardian
İlk Örnek (tüm metin alanlarını göster)
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))
}
}
İkinci Örnek (sadece aktif alanı göster)
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
Bu, üst görünümünün boyutunu ve konumunu emen bir görünümdür. Bunu başarmak için, .background değiştiricisinin içinde çağrılır. Bu çok güçlü bir değiştiricidir, sadece bir görünümün arka planını dekore etmenin bir yolu değildir. Bir görünümü .background'a (MyView ()) aktarırken, MyView üst öğe olarak değiştirilmiş görünümü alıyor. GeometryReader'ı kullanmak, görünümün ebeveynin geometrisini bilmesini mümkün kılan şeydir.
Örneğin: Text("hello").background(GeometryGetter(rect: $bounds))
Değişken sınırları Metin görünümünün boyutu ve konumuyla ve global koordinat alanını kullanarak doldurur.
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)
}
}
}
}
Güncelleme Görünümün işlenirken durumunu değiştirme olasılığını önlemek için DispatchQueue.main.async'i ekledim.
KeyboardGuardian
KeyboardGuardian'ın amacı, klavye gösterme / gizleme olaylarını takip etmek ve görünümün ne kadar alan kaydırılması gerektiğini hesaplamaktır.
Güncelleme: Kullanıcı bir alandan diğerine sekme yaptığında slaytı yenilemek için KeyboardGuardian'ı değiştirdim
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)
}
}
}
}