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

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

【问题讨论】:

    标签: ios swiftui


    【解决方案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 并看到了您的问题,所以我想对此进行一些练习。如果你能接受我的回答,那就太好了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-26
    • 1970-01-01
    • 2019-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多