Ios 如何在SwiftUI中显示键盘时显示完整列表

Ios 如何在SwiftUI中显示键盘时显示完整列表,ios,swift,swiftui,Ios,Swift,Swiftui,当键盘显示时,如何显示完整的列表?键盘正在隐藏列表的下半部分 我的列表行中有一个文本字段。当键盘显示时,无法向下滚动查看完整列表。键盘位于列表的前面,而不是列表的“下方”。这是我的代码: struct ContentView: View { @State private var name = "" var body: some View { List { VStack { Text("Begin")

当键盘显示时,如何显示完整的列表?键盘正在隐藏列表的下半部分

我的列表行中有一个文本字段。当键盘显示时,无法向下滚动查看完整列表。键盘位于列表的前面,而不是列表的“下方”。这是我的代码:

struct ContentView: View {

    @State private var name = ""

    var body: some View {
        List {
            VStack {
                Text("Begin")
                    .frame(width: UIScreen.main.bounds.width)
                    .padding(.bottom, 400)
                    .background(Color.red)

                TextField($name, placeholder: Text("enter text"), onEditingChanged: { _ in
                    //
                }) {
                    //
                }

                Text("End")
                    .frame(width: UIScreen.main.bounds.width)
                    .padding(.top, 400)
                    .background(Color.green)
            }
            .listRowInsets(EdgeInsets())
        }
    }
}
有人能帮我怎么做吗

非常感谢。

有一个用于处理键盘操作的, 您可以订阅如下键盘事件:

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) {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            currentHeight = keyboardSize.height
        }
    }

    @objc func keyBoardWillHide(notification: Notification) {
        currentHeight = 0
    }
}
@State var keyboard = KeyboardResponder()
var body: some View {
        List {
            VStack {
             ...
             ...
             ...
            }.padding(.bottom, keyboard.currentHeight)
}

使用
Compose
KeyboardResponder
对象的替代实现,如图所示

最终类键盘响应器:ObservableObject{
let willChange=PassthroughSubject()
专用(设置)变量currentHeight:长度=0{
意志{
willChange.send(当前高度)
}
}
let keyboard willopen=NotificationCenter.default
.publisher(用于:UIResponder.keyboardWillShowNotification)
.first()//keyboardWillShow通知可能会重复发布
.map{$0.userInfo![UIResponder.keyboardFrameEndUserInfoKey]as!CGRect}
.map{$0.height}
let keyboard willhide=NotificationCenter.default
.publisher(用于:UIResponder.keyboardWillHideNotification)
.map{CGFloat(0)中的{uuu}
func listen(){
_=发布者。合并(keyboardWillOpen,keyboardWillHide)
.subscribe(在:RunLoop.main上)
.assign(到:\.currentHeight,开:self)
}
init(){
听
}
}

更好的方法是将上述内容打包为
视图修改器
(大致改编自):

struct AdaptsToSoftwareKeyboard:ViewModifier{
@状态变量currentHeight:长度=0
func正文(内容:内容)->某些视图{
内容
.padding(.bottom,currentHeight)
.edgesIgnoringSafeArea(currentHeight==0?Edge.Set():.bottom)
.onAppear(执行:subscribeToKeyboardEvents)
}
private let keyboard willopen=NotificationCenter.default
.publisher(用于:UIResponder.keyboardWillShowNotification)
.map{$0.userInfo![UIResponder.keyboardFrameEndUserInfoKey]as!CGRect}
.map{$0.height}
private let keyboard willhide=NotificationCenter.default
.publisher(用于:UIResponder.keyboardWillHideNotification)
.map{长度为.zero}
私有函数subscribeToKeyboardEvents(){
_=发布者。合并(keyboardWillOpen,keyboardWillHide)
.subscribe(在:RunLoop.main上)
.assign(到:\.currentHeight,开:self)
}
}
然后可以这样使用:

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) {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            currentHeight = keyboardSize.height
        }
    }

    @objc func keyBoardWillHide(notification: Notification) {
        currentHeight = 0
    }
}
@State var keyboard = KeyboardResponder()
var body: some View {
        List {
            VStack {
             ...
             ...
             ...
            }.padding(.bottom, keyboard.currentHeight)
}
组{
........
}.modifier(AdaptsToSoftwareKeyboard())

将Bogdan Farca的优秀联合收割机方法更新为XCode 11.2:

import Combine
import SwiftUI

struct AdaptsToSoftwareKeyboard: ViewModifier {

    @State var currentHeight: CGFloat = 0

    func body(content: Content) -> some View {
        content
            .padding(.bottom, self.currentHeight)
            .edgesIgnoringSafeArea(self.currentHeight == 0 ? Edge.Set() : .bottom)
            .onAppear(perform: subscribeToKeyboardEvents)
    }

    private let keyboardWillOpen = NotificationCenter.default
        .publisher(for: UIResponder.keyboardWillShowNotification)
        .map { $0.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect }
        .map { $0.height }

    private let keyboardWillHide =  NotificationCenter.default
        .publisher(for: UIResponder.keyboardWillHideNotification)
        .map { _ in CGFloat.zero }

    private func subscribeToKeyboardEvents() {
        _ = Publishers.Merge(keyboardWillOpen, keyboardWillHide)
            .subscribe(on: RunLoop.main)
            .assign(to: \.self.currentHeight, on: self)
    }
}

让观察者设置一个
环境值
。然后将其作为视图中的变量:

 @Environment(\.keyboardHeight) var keyboardHeight: CGFloat

下面是
BindableObject
实现的更新版本(现在命名为
observeObject

导入快捷界面
进口联合收割机
类KeyboardObserver:ObserveObject{
私有var可取消:任何可取消?
@已发布的专用(设置)var键盘高度:CGFloat=0
let keyboard willshow=NotificationCenter.default
.publisher(用于:UIResponder.keyboardWillShowNotification)
.compactMap{($0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey]as?cRect)?.height}
let keyboard willhide=NotificationCenter.default
.publisher(用于:UIResponder.keyboardWillHideNotification)
.map{{u0->CGFloat in 0}
init(){
Cancelable=发布者。合并(keyboardWillShow,keyboardWillHide)
.subscribe(在:RunLoop.main上)
.分配(至:\.键盘高度,打开:self)
}
}
以下是如何在视图中使用它:

@ObservedObject private var keyboardObserver=keyboardObserver()
var body:一些观点{
...
YourViewYouWantToRaise(你的观点)
.padding(.bottom,keyboardObserver.keyboardHeight)
.animation(.easeInOut(持续时间:0.3))
...
}

这些示例有点旧,我修改了一些代码以使用最近添加到SwiftUI的新功能,本文中可以找到此示例中使用的代码的详细说明:

键盘观察者课程:

用法:

一行解决方案 如果安装,只需在包含列表的视图上调用
.padding(.keyboard)
。这是迄今为止我见过的最好、最简单的解决方案

import SwiftUIX

struct ExampleView: View {
    var body: some View {
        VStack {
           List {
            ForEach(contacts, id: \.self) { contact in
                cellWithContact(contact)
            }
           }
        }.padding(.keyboard) // This is all that's needed, super cool!
    }
}

尝试订阅键盘显示/消失事件,并将底部边距应用于
VStack
我目前正在处理非常类似的事情(使用ScrollView而不是List)。我认为订阅键盘显示/隐藏事件是正确的方法。但这不是困难的部分。挑战在于找出活动文本字段的位置,以确定是否需要偏移量,如果需要,需要多少。你的具体例子会很简单,因为你有一个固定的400像素。。。不过,我假设这只是一个例子。我们的目标是能够确定文本字段相对于其父字段的相对位置,以及它滚动了多少,然后我们知道需要移动多少内容。您尝试过您的答案吗?尽管keyboard.currentHeight会更改,但视图不会移动。使用
.offset(y:-keyboard.currentHeight)
它可以。但是,您的方法完全忽略了文本字段的实际位置。例如,如果列表被滚动,并且字段太高,当键盘出现时,文本字段就会从屏幕上移走。我喜欢你的答案,就是让键盘观察者在一个单独的类上,而不是在视图结构本身上。我可能会在以后更改我发布的答案,以遵循这种方法。我已经尝试了填充,并且效果很好(用户必须手动滚动,但内容将完全显示)。现在我再次阅读OP原始问题,我意识到他想要显示
import SwiftUIX

struct ExampleView: View {
    var body: some View {
        VStack {
           List {
            ForEach(contacts, id: \.self) { contact in
                cellWithContact(contact)
            }
           }
        }.padding(.keyboard) // This is all that's needed, super cool!
    }
}