SwiftUI:提交后留在同一个 TextField 上?
Posted
技术标签:
【中文标题】SwiftUI:提交后留在同一个 TextField 上?【英文标题】:SwiftUI: stay on same TextField after on commit? 【发布时间】:2021-09-17 14:49:27 【问题描述】:即使用户点击键盘上的 Return 键,在 SwiftUI 中是否可以将打字光标保持在同一个 Textfield 上?
这是我的代码:
struct RowView: View
@Binding var checklistItem: ChecklistItem
@ObservedObject var checklist = Checklist()
@ObservedObject var viewModel: ChecklistViewModel
var body: some View
HStack
Button
self.checklistItem.isChecked.toggle()
self.viewModel.updateChecklist(checklistItem)
label:
Circle()
.strokeBorder(checklistItem.isChecked ? checklistSelected : contentPrimary, lineWidth: checklistItem.isChecked ? 6 : 2)
.foregroundColor(backgroundSecondary)
.clipShape(Circle())
.frame(width: 16, height: 16)
.buttonStyle(BorderlessButtonStyle())
// swiftlint:disable trailing_closure
TextField(
"Add...",
text: $checklistItem.name,
onCommit:
do
if !checklistItem.name.isEmpty
self.viewModel.updateChecklist(checklistItem)
self.checklistItem.name = checklistItem.name
)
// swiftlint:enable trailing_closure
.foregroundColor(checklistItem.isChecked ? contentTertiary : contentPrimary)
Spacer()
因此,在用户点击键盘上的返回键后,TextField() onCommit 应该会正常激活,但光标会停留在同一个文本字段中,以便用户可以继续输入新元素。
【问题讨论】:
这可能会有所帮助:***.com/questions/61669540/… 他们如何退出呢?如果键盘一直在,并且无法通过返回关闭,那么用户如何继续? @George 在视图外点击 【参考方案1】:iOS 15+
您可以使用@FocusState
,并在提交时立即将TextField
设置为再次获得焦点。
例子:
struct ContentView: View
@State private var text = "Hello world!"
@FocusState private var isFieldFocused: Bool
var body: some View
Form
TextField("Field", text: $text, onCommit:
isFieldFocused = true
print("onCommit")
)
.focused($isFieldFocused)
结果:
【讨论】:
太棒了,ios 14 中是否有与之等效的版本,因为这是我的最低应用版本,即使它必须是定制的 @Hussein 可能是使用 SwiftUI-Introspect 然后设置 TextField 成为第一响应者。 请注意,从 Xcode 13 开始,焦点行为在 Xcode Previews 中不起作用,但在模拟器中起作用。【参考方案2】:我可以通过创建自定义 TextField 类在 iOS 14 中实现这一点:
struct AlwaysActiveTextField: UIViewRepresentable
let placeholder: String
@Binding var text: String
var focusable: Binding<[Bool]>?
var returnKeyType: UIReturnKeyType = .next
var autocapitalizationType: UITextAutocapitalizationType = .none
var keyboardType: UIKeyboardType = .default
var isSecureTextEntry: Bool
var tag: Int
var onCommit: () -> Void
func makeUIView(context: Context) -> UITextField
let activeTextField = UITextField(frame: .zero)
activeTextField.delegate = context.coordinator
activeTextField.placeholder = placeholder
activeTextField.font = .systemFont(ofSize: 14)
activeTextField.attributedPlaceholder = NSAttributedString(
string: placeholder,
attributes: [NSAttributedString.Key.foregroundColor: UIColor(contentSecondary)]
)
activeTextField.returnKeyType = returnKeyType
activeTextField.autocapitalizationType = autocapitalizationType
activeTextField.keyboardType = keyboardType
activeTextField.isSecureTextEntry = isSecureTextEntry
activeTextField.textAlignment = .left
activeTextField.tag = tag
// toolbar
if keyboardType == .numberPad // keyboard does not have next so add next button in the toolbar
var items = [UIBarButtonItem]()
let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let toolbar: UIToolbar = UIToolbar()
toolbar.sizeToFit()
let nextButton = UIBarButtonItem(title: "Next", style: .plain, target: context.coordinator, action: #selector(Coordinator.showNextTextField))
items.append(contentsOf: [spacer, nextButton])
toolbar.setItems(items, animated: false)
activeTextField.inputAccessoryView = toolbar
// Editin listener
activeTextField.addTarget(context.coordinator, action: #selector(Coordinator.textFieldDidChange(_:)), for: .editingChanged)
return activeTextField
func updateUIView(_ uiView: UITextField, context: Context)
uiView.text = text
if let focusable = focusable?.wrappedValue
if focusable[uiView.tag] // set focused
uiView.becomeFirstResponder()
else // remove keyboard
uiView.resignFirstResponder()
func makeCoordinator() -> Coordinator
Coordinator(self)
final class Coordinator: NSObject, UITextFieldDelegate
let activeTextField: AlwaysActiveTextField
var hasEndedViaReturn = false
weak var textField: UITextField?
init(_ activeTextField: AlwaysActiveTextField)
self.activeTextField = activeTextField
func textFieldDidBeginEditing(_ textField: UITextField)
self.textField = textField
guard let textFieldCount = activeTextField.focusable?.wrappedValue.count else return
var focusable: [Bool] = Array(repeating: false, count: textFieldCount) // remove focus from all text field
focusable[textField.tag] = true // mark current textField focused
activeTextField.focusable?.wrappedValue = focusable
// work around for number pad
@objc
func showNextTextField()
if let textField = self.textField
_ = textFieldShouldReturn(textField)
func textFieldShouldReturn(_ textField: UITextField) -> Bool
hasEndedViaReturn = true
guard var focusable = activeTextField.focusable?.wrappedValue else
textField.resignFirstResponder()
return true
focusable[textField.tag] = true // mark current textField focused
activeTextField.focusable?.wrappedValue = focusable
activeTextField.onCommit()
return true
func textFieldDidEndEditing(_ textField: UITextField)
if !hasEndedViaReturn // user dismisses keyboard
guard let textFieldCount = activeTextField.focusable?.wrappedValue.count else return
// reset all text field, so that makeUIView cannot trigger keyboard
activeTextField.focusable?.wrappedValue = Array(repeating: false, count: textFieldCount)
else
hasEndedViaReturn = false
@objc
func textFieldDidChange(_ textField: UITextField)
activeTextField.text = textField.text ?? ""
并通过添加此@State 变量在 SwiftUI 视图中使用:
@State var fieldFocus: [Bool] = [false]
并在任何等待视图主体的地方添加文本字段代码:
AlwaysActiveTextField(
placeholder: "Add...",
text: $newItemName,
focusable: $fieldFocus,
returnKeyType: .next,
isSecureTextEntry: false,
tag: 0,
onCommit:
print("any action you want on commit")
)
【讨论】:
以上是关于SwiftUI:提交后留在同一个 TextField 上?的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI 如何获取对 List 内编辑 Textfield 的参考?