如何始终在 SwiftUI 中显示键盘

Posted

技术标签:

【中文标题】如何始终在 SwiftUI 中显示键盘【英文标题】:How to always show the keyboard in SwiftUI 【发布时间】:2021-01-02 23:56:24 【问题描述】:

我正在尝试在 SwiftUI 中开发一个填字游戏应用程序。我想总是显示键盘,并且能够选择一个正方形并让按键在那里输入一个字母。有没有办法总是在视图中显示键盘?如果我使用 TextField,我可以让键盘显示出来,但是将拼图的每个单元格设置为文本字段似乎相当笨拙。提前致谢。

【问题讨论】:

【参考方案1】:

最终产品:https://imgur.com/a/Q85GOWj

我决定删除我的其他答案,并尝试练习代码。首先,您需要创建一个始终是第一响应者的 UITextField。你可以这样做:

struct PermanentKeyboard: UIViewRepresentable 
    @Binding var text: String
    
    class Coordinator: NSObject, UITextFieldDelegate 
        var parent: PermanentKeyboard
        
        init(_ parent: PermanentKeyboard) 
            self.parent = parent
        
        
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool 
            
            //Async to prevent updating state during view update
            DispatchQueue.main.async 
                if let last = string.last 
                    
                    //Check if last character is alpha
                    if last.isLetter 
                        
                        //Changes the binding to the last character of input, UPPERCASED
                        self.parent.text = String(last).uppercased()
                        
                    
                    
                //Allows backspace
                 else if string == "" 
                    self.parent.text = ""
                
            
            
            return false
        
    
    
    func makeCoordinator() -> Coordinator 
        Coordinator(self)
    
    
    func makeUIView(context: Context) -> UITextField 
        let textfield = UITextField()
        textfield.delegate = context.coordinator
        
        //Makes textfield invisible
        textfield.tintColor = .clear
        textfield.textColor = .clear
        
        return textfield
    
    
    func updateUIView(_ uiView: UITextField, context: Context) 
        uiView.text = text
        
        //Makes keyboard permanent
        if !uiView.isFirstResponder 
            uiView.becomeFirstResponder()
        
        
        //Reduces space textfield takes up as much as possible
        uiView.setContentHuggingPriority(.defaultHigh, for: .vertical)
        uiView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
    

这被包装在 UIViewRepresentable 中,以便我们可以在 SwiftUI 视图中使用它。当我们的视图可见并且它的键盘永远不会被关闭时,该文本字段将自动聚焦。

现在我们必须使用实际的填字游戏制作 SwiftUI 视图。首先,我将在 ContentView 中定义一个结构,用作填字游戏:

struct CharacterBox: View 
    @Binding var character: String
    @Binding var selectedInput: Int
    let index: Int
    
    var isSelected: Bool 
        index == selectedInput
    
    
    var body: some View 
        ZStack 
            //Required for tap gesture to work
            Rectangle()
                .fill(Color.white)
            
            //To signify selection
            Rectangle()
                .stroke(isSelected ? Color.orange : Color.black)
            
            Text(character)
                .foregroundColor(.black)
        
        .frame(width: 50, height: 50)
        .onTapGesture 
            selectedInput = index
        
    

这个 CharacterBox 结构可以绑定到 ContentView 中的状态变量字符串,如果它与所选的输入变量匹配,则该字符串也将被不可见的文本字段修改。

在 ContentView 上。我们需要为每个要绑定的 CharacterBox 创建一个状态变量,但显然我们不能仅仅手动完成。因此,我们将创建一个数组,并让每个 CharacterBox 根据其索引绑定到一个字符串。

在此之前,我创建了这个函数,它可以帮助我们用一些空字符串初始化一个数组。

func createEmptyStringArray(count: Int) -> [String] 
    var array = [String]()
    
    for _ in 0..<count 
        array.append("")
    
    
    return array

如果你愿意,你可以把它放在 ContentView 之外;它还可以让您以后重用它。然后我们将要绑定的输入定义为这样的数组:

@State private var inputs = createEmptyStringArray(count: 3)
@State private var selectedInput = 0

selectedInput 是不可见文本字段将绑定到的输入数组中字符串的索引。要为每个 CharacterBox 进行绑定,我们可以将此函数放在 ContentView 中:

func getInputBinding(index: Int) -> Binding<String> 
    Binding<String>(
        get: 
            inputs[index]
        ,
        set: 
            inputs[index] = $0
        
    )

请注意,由于您定义的每个 CharacterBox 的索引不应更改,因此可以使用函数来获取这样的 Binding。但是,对于我们的 selectedIndex,我们需要它在每次 selectedIndex 更改时更新绑定,因此我们必须使用计算属性。像这样定义它:

var selectedInputBinding: Binding<String> 
    Binding<String>(
        get: 
            inputs[selectedInput]
        ,
        set: 
            inputs[selectedInput] = $0
        
    )

最后,将它们组合在一起,这就是我们的身体:

var body: some View 
    ZStack 
        PermanentKeyboard(text: selectedInputBinding)
        
        //Hides textfield and prevents user from selecting/pasting/copying
        Rectangle()
            .fill(Color.white)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
        
        //Your crossword with bindings to inputs here; this is hard coded/manually created
        VStack(spacing: 1) 
            ForEach(0 ..< inputs.count)  index in
                CharacterBox(character: getInputBinding(index: index), selectedInput: $selectedInput, index: index)
            
        
    

【讨论】:

只是想说谢谢你的回答。这是我得到的最全面的答案之一,它就像一个魅力。非常感谢您投入所有的细节和努力。 没问题!我正在学习 UITextField 并看到了您的问题,所以我想对此进行一些练习。如果你能接受我的回答,那就太好了。

以上是关于如何始终在 SwiftUI 中显示键盘的主要内容,如果未能解决你的问题,请参考以下文章

键盘在 SwiftUI 视图中始终位于顶部

在 SwiftUI 中显示键盘上方的视图 [关闭]

键盘处理 swiftui

如何使底部按钮跟随 SwiftUI 中的键盘显示

显示键盘时如何隐藏cupertinoTabBar?或者如何让键盘覆盖酒吧?还是将标签栏始终保持在底部?

使 ScrollView 中的滚动条在 SwiftUI 中始终可见