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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 06:06:07