SwiftUI inputAccesoryView 实现 [英] SwiftUI inputAccesoryView Implementation

查看:35
本文介绍了SwiftUI inputAccesoryView 实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 SwiftUI 中的 TextField 上实现 inputAccessoryView.目标是在键盘上方出现一个完成"按钮,按下该按钮后即可摆脱键盘(即 resignFirstResponder()).

I am trying to implement an inputAccessoryView on a TextField in SwiftUI. The goal is to have a "Done" Button appear above the Keyboard which when pressed gets rid of the keyboard (i.e. resignFirstResponder()).

我看到了以下 Medium 文章,该文章声称完全按照我的要求实施此行为,但是,我正在努力使其正常工作.

I came across the following Medium article which purports to implement this behavior exactly as I would require, however, I am struggling to get it working.

媒体链接包含要实现的方法.

我试图在一个空白的 XCode 项目中实现这个,我的代码编译,但是,TextField 永远不会出现,我无法触摸应该调出键盘的区域.如何正确实现此代码以获得所需的行为?

I have tried to implement this in a blank XCode project, my code compiles, however, the TextField never shows up, and I cannot touch in the area it should be to bring up the keyboard. How do I correctly implement this code to get the desired behavior?

    import Foundation
import UIKit
import SwiftUI

class TextFieldViewController
    : UIViewController {

    // our custom text field will report changes to the outside
    let text: Binding<String>?

    // if the toolbar (see below) is used (Done), the keyboard shall be dismissed
    // and optionally we execute a provided closure
    let onDismiss: (() -> Void)?

    init (
        text: Binding<String>
        , onDismiss: (() -> Void)?) {

        self.text = text
        self.onDismiss = onDismiss

        super.init(
            nibName: nil //"<XIB>"
            , bundle: nil //Bundle.main?
        )
    }

    required init?(coder: NSCoder) {
        self.text = nil
        self.onDismiss = nil

        super.init(coder: coder)
    }

    // helper function to encapsulate calling the "view" of UIViewController
    fileprivate func getTextField() -> UITextField? {
        return view as? UITextField
    }

    override func viewDidLoad() {
        let textField = self.getTextField()
        guard textField != nil else {
            return
        }

        // configure a toolbar with a Done button
        let toolbar = UIToolbar()
        toolbar.setItems([
                // just moves the Done item to the right
                UIBarButtonItem(
                    barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace
                    , target: nil
                    , action: nil
                )
                , UIBarButtonItem(
                    title: "Done"
                    , style: UIBarButtonItem.Style.done
                    , target: self
                    , action: #selector(self.onSet)
                )
            ]
            , animated: true
        )
        toolbar.barStyle = UIBarStyle.default
        toolbar.sizeToFit()
        textField?.inputAccessoryView = toolbar
    }

    @objc private func onSet() {
        let textField = self.getTextField()
        textField?.resignFirstResponder()

        self.text?.wrappedValue = textField?.text ?? ""
        self.onDismiss?()
    }

}

// The SwiftUI view, wrapping the UITextField
struct TextFieldView: View {

    var text: Binding<String>
    var onDismissKeyboard: (() -> Void)?

    var body: some View {
        TextFieldRepresentable(
            text: self.text
            , dismissKeyboardCallback: self.onDismissKeyboard
        )
    }
}

// The UIViewControllerRepresentable, feeding and controlling the UIViewController
struct TextFieldRepresentable
    : UIViewControllerRepresentable {

    // the callback
    let dismissKeyboardCallback: (() -> Void)?

    // created in the previous file/gist
    let viewController: TextFieldViewController

    init (
        text: Binding<String>
        , dismissKeyboardCallback: (() -> Void)?) {

        self.dismissKeyboardCallback = dismissKeyboardCallback
        self.viewController = TextFieldViewController(
            text: text
            , onDismiss: dismissKeyboardCallback
        )
    }

    // UIViewControllerRepresentable
    func makeUIViewController(context: Context) -> UIViewController {

        return viewController
    }

    // UIViewControllerRepresentable
    func updateUIViewController(_ viewController: UIViewController, context: Context) {
    }

}

struct ContentView : View {

    @State var email:String = ""

    var body: some View {

        HStack{
        Circle()
        TextFieldView(text: $email)
        Circle()
        }
    }
} 

推荐答案

这里是一个带有自定义工具栏的演示 &输入文本的绑定,但通过排除关闭回调来简化(因为它对于方法演示并不重要),只是为了减少代码.希望能帮到你.

Here is a demo with custom toolbar & binding for entered text, but simplified by excluding on dismiss callback (as it is not important for approach demo), just to have less code. Hope it will be helpful.

import SwiftUI
import UIKit
import Combine

struct CustomInputTextField : UIViewRepresentable {

    @Binding var text: String

    let textField = UITextField(frame: CGRect(x:0, y:0, width: 100, height: 32)) // just any

    func makeUIView(context: UIViewRepresentableContext<CustomInputTextField>) -> UITextField {
        return textField
    }

    func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomInputTextField>) {
        self.textField.text = text
    }

    func makeCoordinator() -> CustomInputTextField.Coordinator {
        let coordinator = Coordinator(self)

        // configure a toolbar with a Done button
        let toolbar = UIToolbar()
        toolbar.setItems([
            // just moves the Done item to the right
            UIBarButtonItem(
                barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace
                , target: nil
                , action: nil
            )
            , UIBarButtonItem(
                title: "Done"
                , style: UIBarButtonItem.Style.done
                , target: coordinator
                , action: #selector(coordinator.onSet)
            )
            ]
            , animated: true
        )
        toolbar.barStyle = UIBarStyle.default
        toolbar.sizeToFit()

        textField.inputAccessoryView = toolbar
        return coordinator
    }

    typealias UIViewType = UITextField

    class Coordinator: NSObject {
        let owner: CustomInputTextField
        private var subscriber: AnyCancellable

        init(_ owner: CustomInputTextField) {
            self.owner = owner
            subscriber = NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: owner.textField)
                .sink(receiveValue: { _ in
                    owner.$text.wrappedValue = owner.textField.text ?? ""
                })
        }

        @objc fileprivate func onSet() {
            owner.textField.resignFirstResponder()
        }

    }
}

struct DemoCustomKeyboardInput : View {

    @State var email:String = ""

    var body: some View {
        VStack{
            CustomInputTextField(text: $email).border(Color.black)
                .padding(.horizontal)
                .frame(maxHeight: 32)
            Divider()
            Text("Entered text: \(email)")
        }
    }
}

struct DemoCustomKeyboardInput_Previews: PreviewProvider {
    static var previews: some View {
        DemoCustomKeyboardInput()
    }
}

这篇关于SwiftUI inputAccesoryView 实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆