Ich habe sieben TextField
in meiner Hauptleitung ContentView
. Wenn der Benutzer die Tastatur öffnet, werden einige davon TextField
unter dem Tastaturrahmen ausgeblendet. Ich möchte also alle nach TextField
oben bewegen , wenn die Tastatur erschienen ist.
Ich habe den folgenden Code verwendet, um TextField
auf dem Bildschirm hinzuzufügen .
struct ContentView : View {
@State var textfieldText: String = ""
var body: some View {
VStack {
TextField($textfieldText, placeholder: Text("TextField1"))
TextField($textfieldText, placeholder: Text("TextField2"))
TextField($textfieldText, placeholder: Text("TextField3"))
TextField($textfieldText, placeholder: Text("TextField4"))
TextField($textfieldText, placeholder: Text("TextField5"))
TextField($textfieldText, placeholder: Text("TextField6"))
TextField($textfieldText, placeholder: Text("TextField6"))
TextField($textfieldText, placeholder: Text("TextField7"))
}
}
}
Ausgabe:
Antworten:
Code aktualisiert für den Xcode, Beta 7.
Sie benötigen keine Auffüllung, ScrollViews oder Listen, um dies zu erreichen. Obwohl diese Lösung auch mit ihnen gut spielen wird. Ich füge hier zwei Beispiele hinzu.
Der erste verschiebt den gesamten TextField nach oben, wenn die Tastatur für einen von ihnen angezeigt wird. Aber nur wenn nötig. Wenn die Tastatur die Textfelder nicht ausblendet, werden sie nicht verschoben.
Im zweiten Beispiel bewegt sich die Ansicht nur so weit, dass das aktive Textfeld nicht ausgeblendet wird.
Beide Beispiele verwenden denselben allgemeinen Code wie am Ende: GeometryGetter und KeyboardGuardian
Erstes Beispiel (alle Textfelder anzeigen)
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)) } }
Zweites Beispiel (nur das aktive Feld anzeigen)
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
Dies ist eine Ansicht, die die Größe und Position der übergeordneten Ansicht absorbiert. Um dies zu erreichen, wird es im Modifikator .background aufgerufen. Dies ist ein sehr leistungsfähiger Modifikator, nicht nur eine Möglichkeit, den Hintergrund einer Ansicht zu dekorieren. Wenn Sie eine Ansicht an .background (MyView ()) übergeben, erhält MyView die geänderte Ansicht als übergeordnetes Element. Die Verwendung von GeometryReader ermöglicht es der Ansicht, die Geometrie des übergeordneten Elements zu kennen.
Beispiel:
Text("hello").background(GeometryGetter(rect: $bounds))
Füllt variable Grenzen mit der Größe und Position der Textansicht und verwendet den globalen Koordinatenraum.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) } } } }
Update Ich habe DispatchQueue.main.async hinzugefügt, um die Möglichkeit zu vermeiden, den Status der Ansicht während des Renderns zu ändern. ***
KeyboardGuardian
Der Zweck von KeyboardGuardian besteht darin, die Ereignisse zum Ein- und Ausblenden der Tastatur zu verfolgen und zu berechnen, wie viel Platz die Ansicht verschoben werden muss.
Update: Ich habe KeyboardGuardian geändert, um die Folie zu aktualisieren, wenn der Benutzer von einem Feld in ein anderes wechselt
import SwiftUI import Combine final class KeyboardGuardian: ObservableObject { public var rects: Array<CGRect> public var keyboardRect: CGRect = CGRect() // keyboardWillShow notification may be posted repeatedly, // this flag makes sure we only act once per keyboard appearance 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) } } } }
quelle
GeometryGetter
als Ansichtsmodifikator als Hintergrund anzuhängen, indem esViewModifier
protokollkonform gemacht wird?.modifier(GeometryGetter(rect: $kGuardian.rects[1]))
statt.background(GeometryGetter(rect: $kGuardian.rects[1]))
. Kein großer Unterschied (nur 2 Zeichen weniger).geometry.frame
von ausDispatchQueue.main.async
mit SIGNAL ABORT half, wird nun Ihre Lösung testen. Update:if geometry.size.width > 0 && geometry.size.height > 0
vor dem Zuweisenself.rect
geholfen.Um auf der Lösung von @rraphael aufzubauen, habe ich sie so konvertiert, dass sie von der heutigen xcode11 swiftUI-Unterstützung verwendet werden kann.
import SwiftUI final class KeyboardResponder: ObservableObject { private var notificationCenter: NotificationCenter @Published private(set) var currentHeight: CGFloat = 0 init(center: NotificationCenter = .default) { notificationCenter = center notificationCenter.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil) notificationCenter.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil) } deinit { notificationCenter.removeObserver(self) } @objc func keyBoardWillShow(notification: Notification) { if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { currentHeight = keyboardSize.height } } @objc func keyBoardWillHide(notification: Notification) { currentHeight = 0 } }
Verwendung:
struct ContentView: View { @ObservedObject private var keyboard = KeyboardResponder() @State private var textFieldInput: String = "" var body: some View { VStack { HStack { TextField("uMessage", text: $textFieldInput) } }.padding() .padding(.bottom, keyboard.currentHeight) .edgesIgnoringSafeArea(.bottom) .animation(.easeOut(duration: 0.16)) } }
Das veröffentlichte
currentHeight
wird ein erneutes Rendern der Benutzeroberfläche auslösen und Ihr TextField nach oben verschieben, wenn die Tastatur angezeigt wird, und nach unten, wenn es geschlossen wird. Ich habe jedoch kein ScrollView verwendet.quelle
.animation(.easeOut(duration: 0.16))
, um zu versuchen, die Geschwindigkeit der Tastatur anzupassen, die nach oben rutscht.keyboardFrameEndUserInfoKey
. Das sollte den letzten Frame für die Tastatur enthalten.Ich habe viele der vorgeschlagenen Lösungen ausprobiert, und obwohl sie in den meisten Fällen funktionieren, hatte ich einige Probleme - hauptsächlich mit dem sicheren Bereich (ich habe ein Formular auf der Registerkarte von TabView).
Am Ende habe ich einige verschiedene Lösungen kombiniert und GeometryReader verwendet, um den unteren Bereich des sicheren Bereichs einer bestimmten Ansicht zu erhalten und ihn für die Berechnung des Polsters zu verwenden:
import SwiftUI import Combine struct AdaptsToKeyboard: ViewModifier { @State var currentHeight: CGFloat = 0 func body(content: Content) -> some View { GeometryReader { geometry in content .padding(.bottom, self.currentHeight) .animation(.easeOut(duration: 0.16)) .onAppear(perform: { NotificationCenter.Publisher(center: NotificationCenter.default, name: UIResponder.keyboardWillShowNotification) .merge(with: NotificationCenter.Publisher(center: NotificationCenter.default, name: UIResponder.keyboardWillChangeFrameNotification)) .compactMap { notification in notification.userInfo?["UIKeyboardFrameEndUserInfoKey"] as? CGRect } .map { rect in rect.height - geometry.safeAreaInsets.bottom } .subscribe(Subscribers.Assign(object: self, keyPath: \.currentHeight)) NotificationCenter.Publisher(center: NotificationCenter.default, name: UIResponder.keyboardWillHideNotification) .compactMap { notification in CGFloat.zero } .subscribe(Subscribers.Assign(object: self, keyPath: \.currentHeight)) }) } } }
Verwendung:
struct MyView: View { var body: some View { Form {...} .modifier(AdaptsToKeyboard()) } }
quelle
Thread 1: signal SIGABRT
online,rect.height - geometry.safeAreaInsets.bottom
wenn ich ein zweites Mal mit der Tastatur zur Ansicht gehe und auf die Schaltfläche klickeTextField
. Es spielt keine Rolle, ob ichTextField
das erste Mal klicke oder nicht. Die App stürzt immer noch ab.Ich habe eine Ansicht erstellt, die jede andere Ansicht umschließen kann, um sie zu verkleinern, wenn die Tastatur angezeigt wird.
Es ist ziemlich einfach. Wir erstellen Publisher für das Ein- und Ausblenden von Tastaturereignissen und abonnieren sie dann mit
onReceive
. Wir verwenden das Ergebnis davon, um ein rechteckiges Tastaturrechteck hinter der Tastatur zu erstellen.struct KeyboardHost<Content: View>: View { let view: Content @State private var keyboardHeight: CGFloat = 0 private let showPublisher = NotificationCenter.Publisher.init( center: .default, name: UIResponder.keyboardWillShowNotification ).map { (notification) -> CGFloat in if let rect = notification.userInfo?["UIKeyboardFrameEndUserInfoKey"] as? CGRect { return rect.size.height } else { return 0 } } private let hidePublisher = NotificationCenter.Publisher.init( center: .default, name: UIResponder.keyboardWillHideNotification ).map {_ -> CGFloat in 0} // Like HStack or VStack, the only parameter is the view that this view should layout. // (It takes one view rather than the multiple views that Stacks can take) init(@ViewBuilder content: () -> Content) { view = content() } var body: some View { VStack { view Rectangle() .frame(height: keyboardHeight) .animation(.default) .foregroundColor(.clear) }.onReceive(showPublisher.merge(with: hidePublisher)) { (height) in self.keyboardHeight = height } } }
Sie können die Ansicht dann folgendermaßen verwenden:
var body: some View { KeyboardHost { viewIncludingKeyboard() } }
Um den Inhalt der Ansicht nach oben zu verschieben, anstatt ihn zu verkleinern, können Sie ihn auffüllen oder versetzen,
view
anstatt ihn in einen VStack mit einem Rechteck zu legen.quelle
self.view
und es funktioniert großartig. Keine Probleme mit der Animationvar body: some View { VStack { view .padding(.bottom, keyboardHeight) .animation(.default) } .onReceive(showPublisher.merge(with: hidePublisher)) { (height) in self.keyboardHeight = height } }
Ich habe einen wirklich einfach zu verwendenden Ansichtsmodifikator erstellt.
Fügen Sie eine Swift-Datei mit dem folgenden Code hinzu und fügen Sie diesen Modifikator einfach Ihren Ansichten hinzu:
import SwiftUI struct KeyboardResponsiveModifier: ViewModifier { @State private var offset: CGFloat = 0 func body(content: Content) -> some View { content .padding(.bottom, offset) .onAppear { NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { notif in let value = notif.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect let height = value.height let bottomInset = UIApplication.shared.windows.first?.safeAreaInsets.bottom self.offset = height - (bottomInset ?? 0) } NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { notif in self.offset = 0 } } } } extension View { func keyboardResponsive() -> ModifiedContent<Self, KeyboardResponsiveModifier> { return modifier(KeyboardResponsiveModifier()) } }
quelle
Oder Sie können einfach IQKeyBoardManagerSwift verwenden
Sie können dies optional Ihrem App-Delegaten hinzufügen, um die Symbolleiste auszublenden und das Ausblenden der Tastatur zu aktivieren, wenn Sie auf eine andere Ansicht als die Tastatur klicken.
IQKeyboardManager.shared.enableAutoToolbar = false IQKeyboardManager.shared.shouldShowToolbarPlaceholder = false IQKeyboardManager.shared.shouldResignOnTouchOutside = true IQKeyboardManager.shared.previousNextDisplayMode = .alwaysHide
quelle
IQKeyboardManager.shared.keyboardDistanceFromTextField
auf 40 erhöhen , um eine angenehme Lücke zu erhalten.IQKeyboardManager.shared.enable = true
, um zu verhindern, dass die Tastatur meine Textfelder verbirgt. In jedem Fall ist dies die beste Lösung. Ich habe 4 Felder vertikal angeordnet und die anderen Lösungen würden für mein unterstes Feld funktionieren, aber das oberste Feld aus dem Blickfeld verschieben.Sie müssen ein hinzufügen
ScrollView
und einen unteren Abstand von der Größe der Tastatur festlegen, damit der Inhalt scrollen kann, wenn die Tastatur angezeigt wird.Um die Tastaturgröße zu erhalten, müssen Sie das verwenden,
NotificationCenter
um sich für das Tastaturereignis zu registrieren. Sie können dazu eine benutzerdefinierte Klasse verwenden:import SwiftUI import Combine final class KeyboardResponder: BindableObject { let didChange = PassthroughSubject<CGFloat, Never>() private var _center: NotificationCenter private(set) var currentHeight: CGFloat = 0 { didSet { didChange.send(currentHeight) } } init(center: NotificationCenter = .default) { _center = center _center.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil) _center.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil) } deinit { _center.removeObserver(self) } @objc func keyBoardWillShow(notification: Notification) { print("keyboard will show") if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue { currentHeight = keyboardSize.height } } @objc func keyBoardWillHide(notification: Notification) { print("keyboard will hide") currentHeight = 0 } }
Mit der
BindableObject
Konformität können Sie diese Klasse als verwendenState
und die Ansichtsaktualisierung auslösen. Schauen Sie sich bei Bedarf das Tutorial an fürBindableObject
: SwiftUI-TutorialWenn Sie das bekommen, müssen Sie a konfigurieren
ScrollView
, um seine Größe zu reduzieren, wenn die Tastatur angezeigt wird. Der Einfachheit halber habe ich diesScrollView
in eine Art Komponente eingewickelt :struct KeyboardScrollView<Content: View>: View { @State var keyboard = KeyboardResponder() private var content: Content init(@ViewBuilder content: () -> Content) { self.content = content() } var body: some View { ScrollView { VStack { content } } .padding(.bottom, keyboard.currentHeight) } }
Jetzt müssen Sie nur noch Ihre Inhalte in die benutzerdefinierte Datei einbetten
ScrollView
.struct ContentView : View { @State var textfieldText: String = "" var body: some View { KeyboardScrollView { ForEach(0...10) { index in TextField(self.$textfieldText, placeholder: Text("TextField\(index)")) { // Hide keyboard when uses tap return button on keyboard. self.endEditing(true) } } } } private func endEditing(_ force: Bool) { UIApplication.shared.keyWindow?.endEditing(true) } }
Bearbeiten: Das Bildlaufverhalten ist wirklich komisch, wenn sich die Tastatur versteckt. Möglicherweise würde dies durch die Verwendung einer Animation zum Aktualisieren des Auffüllens behoben, oder Sie sollten etwas anderes als das verwenden
padding
, um die Größe der Bildlaufansicht anzupassen.quelle
BindableObject
veraltet funktioniert das leider nicht mehr.BindableObject
wurde nur umbenannt inObservableObject
unddidChange
umobjectWillChange
. Das Objekt aktualisiert die Ansicht ganz gut (obwohl ich mit@ObservedObject
statt getestet habe@State
)Xcode 12 - Einzeiliger Code
Fügen Sie diesen Modifikator dem hinzu
TextField
Apple hinzugefügt , um die Tastatur als eine Region für den sicheren Bereich, so dass Sie es verwenden können , bewegen jede
View
mit der Tastatur wie andere Regionen.quelle
View
, einschließlich derTextEditor
..ignoresSafeArea(.keyboard)
auf Ihre Ansicht.Ich habe die vorhandenen Lösungen überprüft und in ein praktisches SPM-Paket umgewandelt, das einen
.keyboardAware()
Modifikator enthält:KeyboardAwareSwiftUI
Beispiel:
struct KeyboardAwareView: View { @State var text = "example" var body: some View { NavigationView { ScrollView { VStack(alignment: .leading) { ForEach(0 ..< 20) { i in Text("Text \(i):") TextField("Text", text: self.$text) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding(.bottom, 10) } } .padding() } .keyboardAware() // <--- the view modifier .navigationBarTitle("Keyboard Example") } } }
Quelle:
import UIKit import SwiftUI public class KeyboardInfo: ObservableObject { public static var shared = KeyboardInfo() @Published public var height: CGFloat = 0 private init() { NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardChanged), name: UIApplication.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardChanged), name: UIResponder.keyboardWillHideNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardChanged), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) } @objc func keyboardChanged(notification: Notification) { if notification.name == UIApplication.keyboardWillHideNotification { self.height = 0 } else { self.height = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.height ?? 0 } } } struct KeyboardAware: ViewModifier { @ObservedObject private var keyboard = KeyboardInfo.shared func body(content: Content) -> some View { content .padding(.bottom, self.keyboard.height) .edgesIgnoringSafeArea(self.keyboard.height > 0 ? .bottom : []) .animation(.easeOut) } } extension View { public func keyboardAware() -> some View { ModifiedContent(content: self, modifier: KeyboardAware()) } }
quelle
Ich habe die Antwort von Benjamin Kindle als Ausgangspunkt verwendet, aber ich hatte einige Probleme, die ich ansprechen wollte.
keyboardWillChangeFrameNotification
zur Liste der verarbeiteten Benachrichtigungen wird dies behoben.init
Funktion bereitgestellt, die a akzeptiert,@ViewBuilder
sodass Sie dieKeyboardHost
Ansicht wie jede andere Ansicht verwenden und Ihren Inhalt einfach in einem nachfolgenden Abschluss übergeben können, anstatt die Inhaltsansicht als Parameter an zu übergebeninit
.Rectangle
um die untere Polsterung anzupassen.UIWindow
as bereitgestellten Zeichenfolgen verwendenUIWindow.keyboardFrameEndUserInfoKey
.Ich habe das alles zusammen:
struct KeyboardHost<Content>: View where Content: View { var content: Content /// The current height of the keyboard rect. @State private var keyboardHeight = CGFloat(0) /// A publisher that combines all of the relevant keyboard changing notifications and maps them into a `CGFloat` representing the new height of the /// keyboard rect. private let keyboardChangePublisher = NotificationCenter.Publisher(center: .default, name: UIResponder.keyboardWillShowNotification) .merge(with: NotificationCenter.Publisher(center: .default, name: UIResponder.keyboardWillChangeFrameNotification)) .merge(with: NotificationCenter.Publisher(center: .default, name: UIResponder.keyboardWillHideNotification) // But we don't want to pass the keyboard rect from keyboardWillHide, so strip the userInfo out before // passing the notification on. .map { Notification(name: $0.name, object: $0.object, userInfo: nil) }) // Now map the merged notification stream into a height value. .map { ($0.userInfo?[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect ?? .zero).size.height } // If you want to debug the notifications, swap this in for the final map call above. // .map { (note) -> CGFloat in // let height = (note.userInfo?[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect ?? .zero).size.height // // print("Received \(note.name.rawValue) with height \(height)") // return height // } var body: some View { content .onReceive(keyboardChangePublisher) { self.keyboardHeight = $0 } .padding(.bottom, keyboardHeight) .animation(.default) } init(@ViewBuilder _ content: @escaping () -> Content) { self.content = content() } } struct KeyboardHost_Previews: PreviewProvider { static var previews: some View { KeyboardHost { TextField("TextField", text: .constant("Preview text field")) } } }
quelle
Keyboard
HöhePridictive
in iOS einschaltenkeyboard
.Settings
->General
->Keyboard
->Pridictive
. In diesem Fall wird das Calclate nicht korrigiert und die Tastatur wirdkeyboardHeight
. Auf meinem iPod Touch (im Hochformat) beträgt eine Tastatur mit eingeschalteter Anzeige 254 Punkte. Ohne es sind 216 Punkte. Ich kann Predictive sogar mit einer Tastatur auf dem Bildschirm ausschalten und die Polsterung wird ordnungsgemäß aktualisiert. Hinzufügen einer Tastatur mit Predictive:Received UIKeyboardWillChangeFrameNotification with height 254.0
Received UIKeyboardWillShowNotification with height 254.0
Wenn ich Predictive Text ausschalte:Received UIKeyboardWillChangeFrameNotification with height 216.0
Dies ist angepasst an das, was @kontiki erstellt hat. Ich habe es in einer App unter Beta 8 / GM Seed ausgeführt, in der das gescrollte Feld Teil eines Formulars in einer Navigationsansicht ist. Hier ist KeyboardGuardian:
// // KeyboardGuardian.swift // // /programming/56491881/move-textfield-up-when-thekeyboard-has-appeared-by-using-swiftui-ios // import SwiftUI import Combine /// The purpose of KeyboardGuardian, is to keep track of keyboard show/hide events and /// calculate how much space the view needs to be shifted. final class KeyboardGuardian: ObservableObject { let objectWillChange = ObservableObjectPublisher() // PassthroughSubject<Void, Never>() public var rects: Array<CGRect> public var keyboardRect: CGRect = CGRect() // keyboardWillShow notification may be posted repeatedly, // this flag makes sure we only act once per keyboard appearance private var keyboardIsHidden = true var slide: CGFloat = 0 { didSet { objectWillChange.send() } } public var showField: Int = 0 { didSet { updateSlide() } } init(textFieldCount: Int) { self.rects = Array<CGRect>(repeating: CGRect(), count: textFieldCount) 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) } @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 { slide = -keyboardRect.size.height } } }
Dann habe ich eine Aufzählung verwendet, um die Slots im rects-Array und die Gesamtzahl zu verfolgen:
enum KeyboardSlots: Int { case kLogPath case kLogThreshold case kDisplayClip case kPingInterval case count }
KeyboardSlots.count.rawValue
ist die notwendige Array-Kapazität; Die anderen als rawValue geben den entsprechenden Index an, den Sie für .background-Aufrufe (GeometryGetter) verwenden.Mit dieser Einstellung erhalten Ansichten beim KeyboardGuardian Folgendes:
@ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: SettingsFormBody.KeyboardSlots.count.rawValue)
Die eigentliche Bewegung ist wie folgt:
.offset(y: kGuardian.slide).animation(.easeInOut(duration: 1))
an die Ansicht angehängt. In meinem Fall ist es an die gesamte Navigationsansicht angehängt, sodass die gesamte Baugruppe nach oben verschoben wird, wenn die Tastatur angezeigt wird.
Ich habe das Problem, mit SwiftUI eine Symbolleiste "Fertig" oder eine Eingabetaste auf einer Dezimaltastatur zu erhalten, nicht gelöst. Stattdessen verwende ich diese Option, um sie an einer anderen Stelle auszublenden:
struct DismissingKeyboard: ViewModifier { func body(content: Content) -> some View { content .onTapGesture { let keyWindow = UIApplication.shared.connectedScenes .filter({$0.activationState == .foregroundActive}) .map({$0 as? UIWindowScene}) .compactMap({$0}) .first?.windows .filter({$0.isKeyWindow}).first keyWindow?.endEditing(true) } } }
Sie hängen es an eine Ansicht als an
.modifier(DismissingKeyboard())
Einige Ansichten (z. B. Picker) mögen es nicht, wenn dies angehängt wird. Daher müssen Sie möglicherweise etwas detaillierter vorgehen, wie Sie den Modifikator anhängen, anstatt ihn nur auf die äußerste Ansicht zu legen.
Vielen Dank an @kontiki für die harte Arbeit. Wie oben in seinen Beispielen dargestellt, benötigen Sie oben noch seinen GeometryGetter (nein, ich habe ihn auch nicht konvertiert, um Einstellungen zu verwenden).
quelle
Einige der oben genannten Lösungen hatten einige Probleme und waren nicht unbedingt der "sauberste" Ansatz. Aus diesem Grund habe ich einige Dinge für die unten stehende Implementierung geändert.
extension View { func onKeyboard(_ keyboardYOffset: Binding<CGFloat>) -> some View { return ModifiedContent(content: self, modifier: KeyboardModifier(keyboardYOffset)) } } struct KeyboardModifier: ViewModifier { @Binding var keyboardYOffset: CGFloat let keyboardWillAppearPublisher = NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification) let keyboardWillHidePublisher = NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification) init(_ offset: Binding<CGFloat>) { _keyboardYOffset = offset } func body(content: Content) -> some View { return content.offset(x: 0, y: -$keyboardYOffset.wrappedValue) .animation(.easeInOut(duration: 0.33)) .onReceive(keyboardWillAppearPublisher) { notification in let keyWindow = UIApplication.shared.connectedScenes .filter { $0.activationState == .foregroundActive } .map { $0 as? UIWindowScene } .compactMap { $0 } .first?.windows .filter { $0.isKeyWindow } .first let yOffset = keyWindow?.safeAreaInsets.bottom ?? 0 let keyboardFrame = (notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue ?? .zero self.$keyboardYOffset.wrappedValue = keyboardFrame.height - yOffset }.onReceive(keyboardWillHidePublisher) { _ in self.$keyboardYOffset.wrappedValue = 0 } } }
struct RegisterView: View { @State var name = "" @State var keyboardYOffset: CGFloat = 0 var body: some View { VStack { WelcomeMessageView() TextField("Type your name...", text: $name).bordered() }.onKeyboard($keyboardYOffset) .background(WelcomeBackgroundImage()) .padding() } }
Ich hätte mir einen saubereren Ansatz gewünscht und die Verantwortung für das Versetzen des Inhalts auf die konstruierte Ansicht (nicht auf den Modifikator) verlagert, aber es scheint, dass ich die Herausgeber nicht dazu bringen konnte, beim Verschieben des Versatzcodes in die Ansicht ordnungsgemäß auszulösen. ...
Beachten Sie auch, dass Publishers in diesem Fall verwendet werden mussten, da
final class
derzeit unbekannte Ausnahmeabstürze verursacht werden (obwohl sie die Schnittstellenanforderungen erfüllen) und eine ScrollView insgesamt der beste Ansatz beim Anwenden von Offset-Code ist.quelle
Viele dieser Antworten scheinen ehrlich gesagt wirklich aufgebläht zu sein. Wenn Sie SwiftUI verwenden, können Sie auch Combine verwenden.
Erstellen Sie eine
KeyboardResponder
wie unten gezeigt, dann können Sie sie wie zuvor gezeigt verwenden.Aktualisiert für iOS 14.
import Combine import UIKit final class KeyboardResponder: ObservableObject { @Published var keyboardHeight: CGFloat = 0 init() { NotificationCenter.default.publisher(for: UIResponder.keyboardWillChangeFrameNotification) .compactMap { notification in (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height } .receive(on: DispatchQueue.main) .assign(to: \.keyboardHeight) } } struct ExampleView: View { @ObservedObject private var keyboardResponder = KeyboardResponder() @State private var text: String = "" var body: some View { VStack { Text(text) Spacer() TextField("Example", text: $text) } .padding(.bottom, keyboardResponder.keyboardHeight) } }
quelle
Ich bin nicht sicher, ob die Übergangs- / Animations-API für SwiftUI vollständig ist, aber Sie könnten
CGAffineTransform
mit verwenden.transformEffect
Erstellen Sie ein beobachtbares Tastaturobjekt mit einer veröffentlichten Eigenschaft wie der folgenden:
final class KeyboardResponder: ObservableObject { private var notificationCenter: NotificationCenter @Published var readyToAppear = false init(center: NotificationCenter = .default) { notificationCenter = center notificationCenter.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil) notificationCenter.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil) } deinit { notificationCenter.removeObserver(self) } @objc func keyBoardWillShow(notification: Notification) { readyToAppear = true } @objc func keyBoardWillHide(notification: Notification) { readyToAppear = false } }
Dann können Sie diese Eigenschaft verwenden, um Ihre Ansicht wie folgt neu zu ordnen:
struct ContentView : View { @State var textfieldText: String = "" @ObservedObject private var keyboard = KeyboardResponder() var body: some View { return self.buildContent() } func buildContent() -> some View { let mainStack = VStack { TextField("TextField1", text: self.$textfieldText) TextField("TextField2", text: self.$textfieldText) TextField("TextField3", text: self.$textfieldText) TextField("TextField4", text: self.$textfieldText) TextField("TextField5", text: self.$textfieldText) TextField("TextField6", text: self.$textfieldText) TextField("TextField7", text: self.$textfieldText) } return Group{ if self.keyboard.readyToAppear { mainStack.transformEffect(CGAffineTransform(translationX: 0, y: -200)) .animation(.spring()) } else { mainStack } } } }
oder einfacher
VStack { TextField("TextField1", text: self.$textfieldText) TextField("TextField2", text: self.$textfieldText) TextField("TextField3", text: self.$textfieldText) TextField("TextField4", text: self.$textfieldText) TextField("TextField5", text: self.$textfieldText) TextField("TextField6", text: self.$textfieldText) TextField("TextField7", text: self.$textfieldText) }.transformEffect(keyboard.readyToAppear ? CGAffineTransform(translationX: 0, y: -50) : .identity) .animation(.spring())
quelle
Xcode 12 Beta 4 fügt einen neuen Ansichtsmodifikator hinzu
ignoresSafeArea
, mit dem Sie jetzt die Tastatur umgehen können..ignoresSafeArea([], edges: [])
Dies vermeidet die Tastatur und alle Kanten des sicheren Bereichs. Sie können den ersten Parameter auf einstellen,
.keyboard
wenn Sie nicht möchten, dass er vermieden wird. Es gibt einige Macken, zumindest in meiner Ansichtshierarchie, aber es scheint, dass Apple auf diese Weise möchte, dass wir die Tastatur meiden.quelle
Antwort von hier kopiert: TextField immer auf der Tastatur mit SwiftUI
Ich habe verschiedene Ansätze ausprobiert und keiner von ihnen hat für mich funktioniert. Dieser unten ist der einzige, der für verschiedene Geräte funktioniert hat.
Fügen Sie diese Erweiterung in eine Datei ein:
import SwiftUI import Combine extension View { func keyboardSensible(_ offsetValue: Binding<CGFloat>) -> some View { return self .padding(.bottom, offsetValue.wrappedValue) .animation(.spring()) .onAppear { NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { notification in let keyWindow = UIApplication.shared.connectedScenes .filter({$0.activationState == .foregroundActive}) .map({$0 as? UIWindowScene}) .compactMap({$0}) .first?.windows .filter({$0.isKeyWindow}).first let bottom = keyWindow?.safeAreaInsets.bottom ?? 0 let value = notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect let height = value.height offsetValue.wrappedValue = height - bottom } NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { _ in offsetValue.wrappedValue = 0 } } } }
In Ihrer Ansicht benötigen Sie eine Variable, um offsetValue zu binden:
struct IncomeView: View { @State private var offsetValue: CGFloat = 0.0 var body: some View { VStack { //... } .keyboardSensible($offsetValue) } }
quelle
NotificationCenter.default.addObserver
... Sie müssen diese speichern und die Beobachter zu einem geeigneten Zeitpunkt entfernen ...Wie Mark Krenek und Heiko hervorgehoben haben, schien Apple dieses Problem in Xcode 12 Beta 4 endlich zu lösen. Die Dinge bewegen sich schnell. Laut den am 18. August 2020 veröffentlichten Versionshinweisen für Xcode 12 Beta 5 "verbergen Formular, Liste und TextEditor keine Inhalte mehr hinter der Tastatur. (66172025)". Ich habe es einfach heruntergeladen und es im Beta 5-Simulator (iPhone SE2) mit einem Formularcontainer in einer App getestet, die ich vor einigen Tagen gestartet habe.
Es funktioniert jetzt "nur" für ein TextField . SwiftUI stellt dem Kapselungsformular automatisch die entsprechende untere Auffüllung zur Verfügung, um Platz für die Tastatur zu schaffen. Das Formular wird automatisch nach oben gescrollt, um das Textfeld direkt über der Tastatur anzuzeigen. Der ScrollView-Container verhält sich jetzt gut, wenn auch die Tastatur aufgerufen wird.
Wie Андрей Первушин in einem Kommentar hervorhob, liegt jedoch ein Problem mit TextEditor vor . Beta 5 & 6 stellen automatisch das entsprechende untere Polster für das Kapselungsformular bereit, um Platz für die Tastatur zu schaffen. Das Formular wird jedoch NICHT automatisch nach oben gescrollt. Die Tastatur deckt den TextEditor ab. Im Gegensatz zu TextField muss der Benutzer also durch das Formular scrollen, um den TextEditor sichtbar zu machen. Ich werde einen Fehlerbericht einreichen. Vielleicht wird Beta 7 das Problem beheben. So nah …
https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-14-beta-release-notes/
quelle
Verwendung:
import SwiftUI var body: some View { ScrollView { VStack { /* TextField() */ } }.keyboardSpace() }
Code:
import SwiftUI import Combine let keyboardSpaceD = KeyboardSpace() extension View { func keyboardSpace() -> some View { modifier(KeyboardSpace.Space(data: keyboardSpaceD)) } } class KeyboardSpace: ObservableObject { var sub: AnyCancellable? @Published var currentHeight: CGFloat = 0 var heightIn: CGFloat = 0 { didSet { withAnimation { if UIWindow.keyWindow != nil { //fix notification when switching from another app with keyboard self.currentHeight = heightIn } } } } init() { subscribeToKeyboardEvents() } private let keyboardWillOpen = NotificationCenter.default .publisher(for: UIResponder.keyboardWillShowNotification) .map { $0.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect } .map { $0.height - (UIWindow.keyWindow?.safeAreaInsets.bottom ?? 0) } private let keyboardWillHide = NotificationCenter.default .publisher(for: UIResponder.keyboardWillHideNotification) .map { _ in CGFloat.zero } private func subscribeToKeyboardEvents() { sub?.cancel() sub = Publishers.Merge(keyboardWillOpen, keyboardWillHide) .subscribe(on: RunLoop.main) .assign(to: \.self.heightIn, on: self) } deinit { sub?.cancel() } struct Space: ViewModifier { @ObservedObject var data: KeyboardSpace func body(content: Content) -> some View { VStack(spacing: 0) { content Rectangle() .foregroundColor(Color(.clear)) .frame(height: data.currentHeight) .frame(maxWidth: .greatestFiniteMagnitude) } } } } extension UIWindow { static var keyWindow: UIWindow? { let keyWindow = UIApplication.shared.connectedScenes .filter({$0.activationState == .foregroundActive}) .map({$0 as? UIWindowScene}) .compactMap({$0}) .first?.windows .filter({$0.isKeyWindow}).first return keyWindow } }
quelle
Handhabung
TabView
‚sIch mag die Antwort von Benjamin Kindle, aber sie unterstützt TabViews nicht. Hier ist meine Anpassung an seinen Code für die Handhabung von TabViews:
UITabView
um die Größe der tabView zu speichern, wenn der Frame festgelegt ist. Wir können dies in einer statischen Variablen speichern, da in einem Projekt normalerweise nur eine tabView vorhanden ist (wenn Ihre mehr als eine hat, müssen Sie sie anpassen).extension UITabBar { static var size: CGSize = .zero open override var frame: CGRect { get { super.frame } set { UITabBar.size = newValue.size super.frame = newValue } } }
onReceive
am unteren Rand derKeyboardHost
Ansicht ändern, um die Höhe der Registerkartenleiste zu berücksichtigen:.onReceive(showPublisher.merge(with: hidePublisher)) { (height) in self.keyboardHeight = max(height - UITabBar.size.height, 0) }
quelle
Ich habe einen völlig anderen Ansatz gewählt, indem ich Folgendes erweitert
UIHostingController
und angepasst habeadditionalSafeAreaInsets
:class MyHostingController<Content: View>: UIHostingController<Content> { override init(rootView: Content) { super.init(rootView: rootView) } @objc required dynamic init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow(_:)), name: UIResponder.keyboardDidShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) } @objc func keyboardDidShow(_ notification: Notification) { guard let info:[AnyHashable: Any] = notification.userInfo, let frame = info[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } // set the additionalSafeAreaInsets let adjustHeight = frame.height - (self.view.safeAreaInsets.bottom - self.additionalSafeAreaInsets.bottom) self.additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: adjustHeight, right: 0) // now try to find a UIResponder inside a ScrollView, and scroll // the firstResponder into view DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) { if let firstResponder = UIResponder.findFirstResponder() as? UIView, let scrollView = firstResponder.parentScrollView() { // translate the firstResponder's frame into the scrollView's coordinate system, // with a little vertical padding let rect = firstResponder.convert(firstResponder.frame, to: scrollView) .insetBy(dx: 0, dy: -15) scrollView.scrollRectToVisible(rect, animated: true) } } } @objc func keyboardWillHide() { self.additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) } } /// IUResponder extension for finding the current first responder extension UIResponder { private struct StaticFirstResponder { static weak var firstResponder: UIResponder? } /// find the current first responder, or nil static func findFirstResponder() -> UIResponder? { StaticFirstResponder.firstResponder = nil UIApplication.shared.sendAction( #selector(UIResponder.trap), to: nil, from: nil, for: nil) return StaticFirstResponder.firstResponder } @objc private func trap() { StaticFirstResponder.firstResponder = self } } /// UIView extension for finding the receiver's parent UIScrollView extension UIView { func parentScrollView() -> UIScrollView? { if let scrollView = self.superview as? UIScrollView { return scrollView } return superview?.parentScrollView() } }
Dann ändern Sie
SceneDelegate
zu verwendenMyHostingController
anstelle vonUIHostingController
.Wenn das erledigt ist, muss ich mich nicht mehr um die Tastatur in meinem SwiftUI-Code kümmern.
(Hinweis: Ich habe dies noch nicht genug verwendet, um die Auswirkungen davon vollständig zu verstehen!)
quelle
So gehe ich mit der Tastatur in SwiftUI um. Beachten Sie, dass die Berechnungen auf dem VStack durchgeführt werden, an den es angehängt ist.
Sie verwenden es in einer Ansicht als Modifikator. Diesen Weg:
struct LogInView: View { var body: some View { VStack { // Your View } .modifier(KeyboardModifier()) } }
Um zu diesem Modifikator zu gelangen, erstellen Sie zunächst eine Erweiterung von UIResponder, um die ausgewählte TextField-Position im VStack abzurufen:
import UIKit // MARK: Retrieve TextField first responder for keyboard extension UIResponder { private static weak var currentResponder: UIResponder? static var currentFirstResponder: UIResponder? { currentResponder = nil UIApplication.shared.sendAction(#selector(UIResponder.findFirstResponder), to: nil, from: nil, for: nil) return currentResponder } @objc private func findFirstResponder(_ sender: Any) { UIResponder.currentResponder = self } // Frame of the superview var globalFrame: CGRect? { guard let view = self as? UIView else { return nil } return view.superview?.convert(view.frame, to: nil) } }
Sie können den KeyboardModifier jetzt mit Combine erstellen, um zu vermeiden, dass eine Tastatur ein TextField verbirgt:
import SwiftUI import Combine // MARK: Keyboard show/hide VStack offset modifier struct KeyboardModifier: ViewModifier { @State var offset: CGFloat = .zero @State var subscription = Set<AnyCancellable>() func body(content: Content) -> some View { GeometryReader { geometry in content .padding(.bottom, self.offset) .animation(.spring(response: 0.4, dampingFraction: 0.5, blendDuration: 1)) .onAppear { NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification) .handleEvents(receiveOutput: { _ in self.offset = 0 }) .sink { _ in } .store(in: &self.subscription) NotificationCenter.default.publisher(for: UIResponder.keyboardWillChangeFrameNotification) .map(\.userInfo) .compactMap { ($0?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.size.height } .sink(receiveValue: { keyboardHeight in let keyboardTop = geometry.frame(in: .global).height - keyboardHeight let textFieldBottom = UIResponder.currentFirstResponder?.globalFrame?.maxY ?? 0 self.offset = max(0, textFieldBottom - keyboardTop * 2 - geometry.safeAreaInsets.bottom) }) .store(in: &self.subscription) } .onDisappear { // Dismiss keyboard UIApplication.shared.windows .first { $0.isKeyWindow }? .endEditing(true) self.subscription.removeAll() } } } }
quelle
Was iOS 14 (Beta 4) betrifft, funktioniert es ganz einfach:
var body: some View { VStack { TextField(...) } .padding(.bottom, 0) }
Die Größe der Ansicht wird an die Oberseite der Tastatur angepasst. Mit dem Rahmen (.maxHeight: ...) usw. sind sicherlich weitere Verfeinerungen möglich. Sie werden es herausfinden.
Leider verursacht die schwebende Tastatur auf dem iPad beim Verschieben immer noch Probleme. Aber die oben genannten Lösungen würden auch, und es ist noch Beta, ich hoffe, sie werden es herausfinden.
Danke Apple endlich!
quelle
Meine Sicht:
struct AddContactView: View { @Environment(\.presentationMode) var presentationMode : Binding<PresentationMode> @ObservedObject var addContactVM = AddContactVM() @State private var offsetValue: CGFloat = 0.0 @State var firstName : String @State var lastName : String @State var sipAddress : String @State var phoneNumber : String @State var emailID : String var body: some View { VStack{ Header(title: StringConstants.ADD_CONTACT) { self.presentationMode.wrappedValue.dismiss() } ScrollView(Axis.Set.vertical, showsIndicators: false){ Image("contactAvatar") .padding(.top, 80) .padding(.bottom, 100) //.padding(.vertical, 100) //.frame(width: 60,height : 60).aspectRatio(1, contentMode: .fit) VStack(alignment: .center, spacing: 0) { TextFieldBorder(placeHolder: StringConstants.FIRST_NAME, currentText: firstName, imageName: nil) TextFieldBorder(placeHolder: StringConstants.LAST_NAME, currentText: lastName, imageName: nil) TextFieldBorder(placeHolder: StringConstants.SIP_ADDRESS, currentText: sipAddress, imageName: "sipPhone") TextFieldBorder(placeHolder: StringConstants.PHONE_NUMBER, currentText: phoneNumber, imageName: "phoneIcon") TextFieldBorder(placeHolder: StringConstants.EMAILID, currentText: emailID, imageName: "email") } Spacer() } .padding(.horizontal, 20) } .padding(.bottom, self.addContactVM.bottomPadding) .onAppear { NotificationCenter.default.addObserver(self.addContactVM, selector: #selector(self.addContactVM.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self.addContactVM, selector: #selector(self.addContactVM.keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil) } } }
Meine VM:
class AddContactVM : ObservableObject{ @Published var contact : Contact = Contact(id: "", firstName: "", lastName: "", phoneNumbers: [], isAvatarAvailable: false, avatar: nil, emailID: "") @Published var bottomPadding : CGFloat = 0.0 @objc func keyboardWillShow(_ notification : Notification){ if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { let keyboardRectangle = keyboardFrame.cgRectValue let keyboardHeight = keyboardRectangle.height self.bottomPadding = keyboardHeight } } @objc func keyboardWillHide(_ notification : Notification){ self.bottomPadding = 0.0 } }
Grundsätzlich wird die untere Polsterung basierend auf der Tastaturhöhe verwaltet.
quelle
Die eleganteste Antwort, die ich darauf erhalten habe, ähnelt der von Rraphael. Erstellen Sie eine Klasse, um auf Tastaturereignisse zu warten. Anstatt die Tastaturgröße zum Ändern der Auffüllung zu verwenden, geben Sie einen negativen Wert der Tastaturgröße zurück und passen Sie mit dem Modifikator .offset (y :) den Versatz der äußersten Ansichtscontainer an. Es animiert gut genug und funktioniert mit jeder Ansicht.
quelle
.offset(y: withAnimation { -keyboard.currentHeight })
, aber der Inhalt springt statt animiert.