You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI多行TextField点击Done键无法收起键盘且触发换行问题

解决SwiftUI TextField点击Done按钮换行而非收起键盘的问题

问题根源

设置axis: .vertical后,SwiftUI的TextField会切换为多行输入模式(底层基于UITextView实现),此时系统默认将Done按钮的行为设为插入换行,而非触发提交事件,这直接导致.submitLabel(.done)和.onSubmit完全失效。

解决方案

根据你完全禁止换行、点击Done收起键盘的需求,提供两种可行方案:

方案1:移除垂直轴设置(推荐,无需自定义组件)

如果仅需要内容过长时自动换行,不需要手动输入换行,直接去掉axis: .vertical即可。默认的水平轴TextField会将Done按钮绑定到提交事件:

TextField(
    "Describe...",
    text: $text
)
.focused(focused, equals: .prompt)
.textFieldStyle(.roundedBorder)
.lineLimit(2...) // 内容过长时自动换行,禁止手动输入换行
.modifier(ClearButton(text: $text))
.submitLabel(.done)
.onSubmit {
    hideKeyboard()
    print("test")
}

方案2:自定义TextField(强制控制Done行为)

如果必须保留axis: .vertical的特性,需要基于UIKit封装自定义TextField,完全拦截换行输入并控制Done按钮行为:

import SwiftUI
import UIKit

struct NoNewLineTextField: UIViewRepresentable {
    let placeholder: String
    @Binding var text: String
    let onDone: () -> Void
    
    func makeUIView(context: Context) -> UITextField {
        let textField = UITextField()
        textField.placeholder = placeholder
        textField.borderStyle = .roundedRect
        textField.delegate = context.coordinator
        textField.returnKeyType = .done
        textField.numberOfLines = 0
        textField.lineBreakMode = .byWordWrapping
        textField.maximumNumberOfLines = 2
        return textField
    }
    
    func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.text = text
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UITextFieldDelegate {
        let parent: NoNewLineTextField
        
        init(_ parent: NoNewLineTextField) {
            self.parent = parent
        }
        
        // 拦截换行输入
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            if string == "\n" {
                parent.onDone()
                return false
            }
            let newText = (textField.text as NSString?)?.replacingCharacters(in: range, with: string) ?? ""
            parent.text = newText.replacingOccurrences(of: "\n", with: "")
            return true
        }
        
        // Done按钮触发时收起键盘
        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
            parent.onDone()
            textField.resignFirstResponder()
            return false
        }
    }
}

// 使用示例
struct ContentView: View {
    @State private var text = ""
    @FocusState private var focused: Field?
    
    enum Field: Hashable {
        case prompt
    }
    
    func hideKeyboard() {
        focused = nil
    }
    
    var body: some View {
        NoNewLineTextField(
            placeholder: "Describe...",
            text: $text,
            onDone: {
                hideKeyboard()
                print("test")
            }
        )
        .focused(focused, equals: .prompt)
        .frame(height: 60)
    }
}

内容的提问来源于stack exchange,提问作者erotsppa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 00:13:26