是否可以使用 SwiftUI 将键盘上的“返回”键更改为“完成”?

Posted

技术标签:

【中文标题】是否可以使用 SwiftUI 将键盘上的“返回”键更改为“完成”?【英文标题】:Is it possible to change "return" key to "done" on keyboard with SwiftUI? 【发布时间】:2020-01-31 17:20:37 【问题描述】:

在 SwiftUI 中为 TextField 打开键盘时,我没有找到任何链接或指南来将“返回”键更改为“完成”。

现在可以不自定义 UITextField 吗?

【问题讨论】:

这能回答你的问题吗? Change 'Return' button function to 'Done' in swift in UITextView 不,他们正在使用 UIKit,我正在寻找 SwiftUI 的答案 我正在寻找 ***.com/questions/58121756/… 但没有 UIKit。 跳到下面 Ty Irvine 的回答,我认为是迄今为止最简单的解决方案 【参考方案1】:

ios 15

您可以使用一个名为 submitLabel 的简单修饰符更改每个 textField 的返回键,该修饰符采用返回键类型:

来自 WWDC21 的图片

此外,如您所见,您可以使用回调来处理返回键按下操作,就像 .onSubmit 修饰符可以访问的旧 textFieldShouldReturn 函数一样。

⚠️ 似乎 Xcode 13 beta 1 中存在一个错误,导致此修饰符在某些情况下无法正常工作。

【讨论】:

它对你有用吗?它不适合我,我想知道是否有错误或什么...... 你检查过 iOS 15 吗? 好的,我只是发现它在工作表中不起作用。没有工作表,它工作正常。【参考方案2】:

更新

这不再是一个好的解决方案。在 iOS 15 中,您现在可以添加 .submitLabel(.done) 修饰符。有关详细信息,请参阅 Mojtaba 的回答。


旧答案

我发现最好的方法是将包 Introspect 添加到您的项目中。

这样做后,在项目文件的任何位置添加import Introspect

然后将他们的视图修饰符之一添加到您的Textfield 以实现您想要的。我相信这就是你想要的 ⤵

.introspectTextField  textfield in
  textfield.returnKeyType = .done

内省是做什么的?

它公开了 UIKit 以在 SwiftUI 中使用。因此,您在上面看到的 Textfield 对象可以访问所有 UITextfield 功能!虽然这是一个软件包,但请注意它可能会在未来损坏,但目前这是一个不错的选择。

这很好,因为它使您不必为每个视图制作自己的 UIKit 包装器?

【讨论】:

这是一个很棒的 pod,谢谢分享。我真的不喜欢这样的解决方法,我认为这是一种天才的方法,直到这些东西可以更自然地在 SwiftUI 中访问。 这应该被选为正确答案。【参考方案3】:

如果有人想在 UIViewRepresentable 中包装 UITextField,那么我有一些代码可以分享:

struct CustomTextField: UIViewRepresentable 

    let tag: Int
    let placeholder: String
    let keyboardType: UIKeyboardType
    let returnVal: UIReturnKeyType

    @Binding var text: String
    @Binding var activeFieldTag: Int?
    var totalFields: Int = 0
    @Binding var isSecureTextEntry: Bool
    var textColor: UIColor = .pureWhite
    var font: UIFont = .nexaBold13
    var placeholderTextColor: UIColor = .pureWhite
    var placeholderFont: UIFont = .nexaLight13
    var onEditingChanged: (Bool) -> Void =  _ in 

    var lastActiveFieldTag: Int? 

        // Return, if no active field
        // (It also means textFieldShouldReturn not called yet OR called for last field)
        guard let activeFieldTag = activeFieldTag else 
            return nil
        
        // Return previous field
        if activeFieldTag > 0 
            return activeFieldTag - 1
        
        // Return, if no previous field
        return nil
    

    func makeUIView(context: Context) -> UITextField 
        let textField = UITextField(frame: .zero)
        textField.keyboardType = self.keyboardType
        textField.returnKeyType = self.returnVal
        textField.tag = self.tag
        textField.textColor = textColor
        textField.font = font
        textField.attributedPlaceholder = NSAttributedString(
            string: self.placeholder,
            attributes: [
                NSAttributedString.Key.foregroundColor: placeholderTextColor,
                NSAttributedString.Key.font: placeholderFont,
            ]
        )
        textField.delegate = context.coordinator
        textField.autocorrectionType = .no
        textField.isSecureTextEntry = isSecureTextEntry
        return textField
    

    func updateUIView(_ textField: UITextField, context: Context) 
        if textField.text != self.text 
            textField.text = self.text
        
        handleFirstResponder(textField)
        if textField.isSecureTextEntry != isSecureTextEntry 
            textField.isSecureTextEntry = isSecureTextEntry
        
    

    func handleFirstResponder(_ textField: UITextField) 

        // return if field is neither active nor last-active
        if tag != lastActiveFieldTag && tag != activeFieldTag 
            return
        

        // return if field is already active
        if lastActiveFieldTag == activeFieldTag 
            return
        

        // It creates problem in UI when we press the next button too fast and continuously on keyboard
        //        // Remove focus from last active field
        //        if lastActiveFieldTag == tag 
        //            uiView.removeFocus()
        //            return
        //        

        // Give focus to active field
        if activeFieldTag == tag 
            textField.focus()
            return
        
    

    // Its called when pressing Next button on the keyboard
    // See textFieldShouldReturn
    func updateNextTag() 
        // There is no next field so set activeFieldTag to nil
        if tag + 1 == totalFields 
            activeFieldTag = nil
         else 
            // Set next field tag as active
            activeFieldTag = tag + 1
        
    

    func makeCoordinator() -> Coordinator 
        Coordinator(self)
    

    class Coordinator: NSObject, UITextFieldDelegate 

        var parent: CustomTextField

        init(_ textField: CustomTextField) 
            self.parent = textField
        

        func updatefocus(textfield: UITextField) 
            textfield.focus()
        

        func textFieldShouldReturn(_ textField: UITextField) -> Bool 

            // Give focus to next field
            parent.updateNextTag()
            parent.text = textField.text ?? ""

            // If there is no next active field then dismiss the keyboard
            if parent.activeFieldTag == nil 
                DispatchQueue.main.async 
                    textField.removeFocus()
                
             

            return true
        

        func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool 
            DispatchQueue.main.async 
                // To enable user to click on any textField while another is active
                self.parent.activeFieldTag = self.parent.tag
                self.parent.onEditingChanged(true)
            
            return true
        

        func textFieldShouldEndEditing(_ textField: UITextField) -> Bool 
            self.parent.text = textField.text ?? ""
            DispatchQueue.main.async 
                self.parent.onEditingChanged(false)
            
            return true
        

        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool 
            if let text = textField.text, let rangeExp = Range(range, in: text) 
                self.parent.text = text.replacingCharacters(in: rangeExp, with: string)
            
            return true
        
    

【讨论】:

为什么我收到错误消息说Type 'UIColor' has no member 'pureWhite' 并且“UITextField”类型的值没有成员“焦点”?有什么变化【参考方案4】:

获取键盘上的go 按钮。尝试将键盘类型更改为 .webSearch。

// 在 Xcode 12 beta 2 和 iOS 14 上测试

.keyboardType(.webSearch)

【讨论】:

很好的答案。对于记录在here 中的不同用例,还有其他很好的选择,例如。 .keyboardType(.twitter).keyboardType(.phonePad)【参考方案5】:

iOS 15.0+

macOS 12.0+, Mac 催化剂 15.0+, tvOS 15.0+, watchOS 8.0+

submitLabel(_:)

为此视图设置提交标签。

https://developer.apple.com

Form 
    TextField("Username", $viewModel.username)
        .submitLabel(.done)

【讨论】:

以上是关于是否可以使用 SwiftUI 将键盘上的“返回”键更改为“完成”?的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 从 UITextField 中关闭键盘

SwiftUI:提交后留在同一个 TextField 上?

如何手动启用或禁用键盘上的 Return 键?

如何让 iPhone 上的返回键使键盘消失?

如何使用 jQuery 检测键盘上的 Enter 键?

iOS:数字键盘上是不是可以有返回键?