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

ste*_*CDP 7 ios swift swiftui

键盘出现时如何显示完整的列表?键盘隐藏在列表的下部。

我的列表行中有一个textField。当键盘出现时,无法向下滚动以查看完整列表。键盘在列表的前面,而不在列表的“下面”。这是我的编码:

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())
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有人可以帮我怎么做吗?

非常感谢你。

Bog*_*rca 10

另一种实现的KeyboardResponder使用对象Compose,因为看到这里

final class KeyboardResponder: BindableObject {

    let willChange = PassthroughSubject<CGFloat, Never>()

    private(set) var currentHeight: Length = 0 {
        willSet {
            willChange.send(currentHeight)
        }
    }

    let keyboardWillOpen = NotificationCenter.default
        .publisher(for: UIResponder.keyboardWillShowNotification)
        .first() // keyboardWillShow notification may be posted repeatedly
        .map { $0.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect }
        .map { $0.height }

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

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

    init() {
        listen()
    }
}
Run Code Online (Sandbox Code Playgroud)

更好的方法是将上述内容打包为ViewModifier(从此处宽松地修改):

struct AdaptsToSoftwareKeyboard: ViewModifier {

    @State var currentHeight: Length = 0

    func body(content: Content) -> some View {
        content
            .padding(.bottom, currentHeight)
            .edgesIgnoringSafeArea(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 Length.zero }

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

然后可以这样使用:

Group {

   ........

}.modifier(AdaptsToSoftwareKeyboard())
Run Code Online (Sandbox Code Playgroud)

  • 该修改器是一个优雅的解决方案,还有一个关于如何使用新的 **Combine** 框架的很好的示例。在 Xcode 版本 11.0 (11A420a) 上,我必须将对 Length 的引用更改为 CGFloat。干得好,谢谢! (3认同)
  • 我喜欢 ViewModifier 方法,但是,与其将其应用于整个组,不如能够将其应用于层次结构中的任何任意视图以表示“此视图不得被键盘遮挡”,在表单中,它始终是活动的文本字段,但对于登录屏幕来说,它可能是用户名、密码和登录按钮。 (2认同)

Far*_*esh 5

这里有一个处理键盘操作的答案,您可以订阅这样的键盘事件:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

@State var keyboard = KeyboardResponder()
var body: some View {
        List {
            VStack {
             ...
             ...
             ...
            }.padding(.bottom, keyboard.currentHeight)
}
Run Code Online (Sandbox Code Playgroud)


Gen*_*gan 5

将Bogdan Farca出色的Combine方法更新为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)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在 iPad 上无法正常工作。添加额外的(不知道,但大约 20-40px)填充,显示为内容和键盘之间的空白 (2认同)

小智 5

这是实现的更新版本BindableObject(现在命名为ObservableObject)。

import SwiftUI
import Combine

class KeyboardObserver: ObservableObject {

  private var cancellable: AnyCancellable?

  @Published private(set) var keyboardHeight: CGFloat = 0

  let keyboardWillShow = NotificationCenter.default
    .publisher(for: UIResponder.keyboardWillShowNotification)
    .compactMap { ($0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.height }

  let keyboardWillHide = NotificationCenter.default
    .publisher(for: UIResponder.keyboardWillHideNotification)
    .map { _ -> CGFloat in 0 }

  init() {
    cancellable = Publishers.Merge(keyboardWillShow, keyboardWillHide)
      .subscribe(on: RunLoop.main)
      .assign(to: \.keyboardHeight, on: self)
  }
}
Run Code Online (Sandbox Code Playgroud)

以下是在您的视图中使用它的方法:

@ObservedObject private var keyboardObserver = KeyboardObserver()

var body: some View {
  ...
  YourViewYouWantToRaise()
    .padding(.bottom, keyboardObserver.keyboardHeight)
    .animation(.easeInOut(duration: 0.3))
  ...
}
Run Code Online (Sandbox Code Playgroud)