SwiftUI列表中的键盘侦听器

时间:2019-09-02 07:39:18

标签: ios swiftui

我有一个列表,其中包含5个文本字段,一个图像和一个按钮的形式。以下是来自AppCoda.的引用我在这里面临的问题是,当键盘弹起时,我无法显示最后一个文本字段。找到了许多相关的答案,但没有一个有帮助。另外,我想在单击或长按时从列表中删除动画。

Video Here

任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:1)

我有您有问题添加的结帐链接。 Blaaahhhh ,由于某些语法而非常令人讨厌。无论如何,我不会解释您已经完成的整个教程。

我已从此处引用:https://stackoverflow.com/a/56721268/6144643

如下更新LabelTextField代码。

struct LabelTextField: View {

    var label: String = ""
    var placeHolder: String = ""
    var tag: Int? = nil
    var kGuardian: KeyboardGuardian
    @State var textField: String = ""

    var body: some View {
        VStack(alignment: .leading) {
            Text(label)
                .font(.headline)
            TextField(placeHolder, text: $textField, onEditingChanged: {
                if $0 { self.kGuardian.showField = self.tag! }
            })
                .padding(.all)
                .background(Color(red: 239.0/255.0, green: 243.0/255.0, blue: 244.0/255.0))
                .cornerRadius(5.0)
            }
        .padding()
        .listRowInsets(EdgeInsets())
    }
}

按以下步骤更新ContentView中的代码以初始化LabelTextField

import SwiftUI
import Combine

struct ContentView: View {

    @ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 5)

    var body: some View {

        NavigationView {
            List {

                VStack(alignment: .leading) {
                    LabelTextField(label: "NAME", placeHolder: "Fill in the restaurant name", tag: 0, kGuardian: kGuardian, textField: "")
                        .background(GeometryGetter(rect: $kGuardian.rects[0]).foregroundColor(.clear))

                    LabelTextField(label: "TYPE", placeHolder: "Fill in the restaurant type", tag: 1, kGuardian: kGuardian, textField: "")
                        .background(GeometryGetter(rect: $kGuardian.rects[1]).foregroundColor(.clear))

                    LabelTextField(label: "ADDRESS", placeHolder: "Fill in the restaurant address", tag: 2, kGuardian: kGuardian, textField: "")
                        .background(GeometryGetter(rect: $kGuardian.rects[2]).foregroundColor(.clear))

                    LabelTextField(label: "PHONE", placeHolder: "Fill in the restaurant phone", tag: 3, kGuardian: kGuardian, textField: "")
                        .background(GeometryGetter(rect: $kGuardian.rects[3]).foregroundColor(.clear))

                    LabelTextField(label: "DESCRIPTION", placeHolder: "Fill in the restaurant description", tag: 4, kGuardian: kGuardian, textField: "")
                        .background(GeometryGetter(rect: $kGuardian.rects[4]).foregroundColor(.clear))

                    RoundedButton().padding(.top, 20)
                }
                .padding(.top, 20)
                .listRowInsets(EdgeInsets())
                .offset(y: kGuardian.slide).animation(Animation.linear(duration: 0.3))
            }
            .navigationBarTitle(Text("New Restaurant"), displayMode: .inline)
            .navigationBarItems(trailing:
                Button(action: {

                }, label: {
                    Text("Cancel")
                })
            )
        }
    }
}

struct RoundedButton: View {
    var body: some View {
        Button(action: {}) {
            HStack {
                Spacer()
                Text("Save")
                    .font(.headline)
                    .foregroundColor(.white)
                Spacer()
            }
        }
        .padding(.vertical, 10.0)
        .background(Color.red)
        .cornerRadius(4.0)
        .padding(.horizontal, 50)
    }
}

添加以下代码,这些代码吸收了其父视图的大小和位置。

struct GeometryGetter: View {
    @Binding var rect: CGRect

    var body: some View {
        GeometryReader { geometry in
            Group { () -> ShapeView<Rectangle, Color> in
                DispatchQueue.main.async {
                    self.rect = geometry.frame(in: .global)
                }

                return Rectangle().fill(Color.clear) as! ShapeView<Rectangle, Color>
            }
        }
    }
}

然后为键盘隐藏/显示通知添加以下代码。

final class KeyboardGuardian: ObservableObject {

    let objectWillChange = 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
    public var keyboardIsHidden = true

    public 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)
    }

    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
            print("tfRect", tfRect, "\nself.showField", self.showField)
            if diff > 0 {
                slide += diff
            } else {
                slide += min(diff, 0)
            }
        }
    }
}