如何在tvOS中使用SwiftUI自定义TextField组件?
在tvOS上自定义SwiftUI TextField的实现方案
针对你提到的需求,以下是具体实现方式,覆盖带描述文本的差异化样式、自定义提交按钮、空文本禁用按钮,以及解决inputAccessoryView点击无响应的问题:
一、实现带描述文本的差异化样式
通过组合SwiftUI原生视图,模拟Apple设置界面的TextField布局,区分标题与描述的颜色层级:
import SwiftUI struct StyledTextField: View { let title: String let description: String @Binding var text: String var body: some View { VStack(alignment: .leading, spacing: 4) { Text(title) .font(.headline) .foregroundColor(.primary) Text(description) .font(.caption) .foregroundColor(.secondary) TextField("", text: $text) .textFieldStyle(.roundedBorder) .padding(.top, 4) } .padding(.horizontal) } } // 使用示例 struct ContentView: View { @State private var text = "" var body: some View { StyledTextField( title: "服务器地址", description: "输入你的远程服务器IP或域名", text: $text ) } }
二、自定义提交按钮并实现空文本禁用
由于SwiftUI原生TextField无法直接控制提交按钮的禁用状态,可通过UIViewRepresentable封装UITextField,自定义inputAccessoryView并绑定文本状态:
import SwiftUI import UIKit struct CustomTextField: UIViewRepresentable { let title: String let submitButtonTitle: String @Binding var text: String var onSubmit: () -> Void func makeUIView(context: Context) -> UITextField { let textField = UITextField() textField.placeholder = title textField.borderStyle = .roundedRect // 构建自定义inputAccessoryView let accessoryView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50)) accessoryView.backgroundColor = .systemBackground let submitButton = UIButton(type: .system) submitButton.setTitle(submitButtonTitle, for: .normal) submitButton.addTarget(context.coordinator, action: #selector(Coordinator.submitTapped), for: .primaryActionTriggered) submitButton.translatesAutoresizingMaskIntoConstraints = false accessoryView.addSubview(submitButton) NSLayoutConstraint.activate([ submitButton.centerYAnchor.constraint(equalTo: accessoryView.centerYAnchor), submitButton.trailingAnchor.constraint(equalTo: accessoryView.trailingAnchor, constant: -20) ]) textField.inputAccessoryView = accessoryView textField.delegate = context.coordinator return textField } func updateUIView(_ uiView: UITextField, context: Context) { uiView.text = text // 根据文本是否为空切换按钮启用状态 if let submitButton = uiView.inputAccessoryView?.subviews.first as? UIButton { submitButton.isEnabled = !text.isEmpty } } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UITextFieldDelegate { let parent: CustomTextField init(_ parent: CustomTextField) { self.parent = parent } @objc func submitTapped() { parent.onSubmit() } func textFieldDidChangeSelection(_ textField: UITextField) { parent.text = textField.text ?? "" } } } // 使用示例 struct ContentView: View { @State private var serverAddress = "" var body: some View { VStack(alignment: .leading, spacing: 4) { Text("服务器地址") .font(.headline) .foregroundColor(.primary) Text("输入你的远程服务器IP或域名") .font(.caption) .foregroundColor(.secondary) CustomTextField( title: "服务器地址", submitButtonTitle: "连接", text: $serverAddress ) { // 提交动作逻辑 print("提交服务器地址: \(serverAddress)") } .frame(height: 44) .padding(.top, 4) } .padding(.horizontal) .focusable() } }
三、解决inputAccessoryView点击无响应的问题
tvOS中UIButton需绑定.primaryActionTriggered事件而非touchUpInside,因为tvOS依赖焦点与遥控器点击触发交互,上述封装代码已使用该事件。同时需确保TextField所在视图标记为focusable(),保证能正确获取焦点并触发输入交互。
内容的提问来源于stack exchange,提问作者Rajmund Zawiślak
相关产品推荐
相关产品推荐

