import SwiftUI // MARK: - 通用文本输入组件 struct TextInputView: View { @Binding var content: String @FocusState var isContentFieldFocused: Bool let placeholder: String let maxCharacters: Int var body: some View { VStack(spacing: 8) { ZStack { // 输入框主体 TextEditor(text: $content) .frame(minHeight: 120) .padding(8) .background(Color(.systemBackground)) .cornerRadius(8) .overlay( RoundedRectangle(cornerRadius: 8) .stroke(isContentFieldFocused ? Color.blue : Color(.systemGray4), lineWidth: 1) ) .focused($isContentFieldFocused) .onChange(of: content) { newValue in // 限制最大字符数 if newValue.count > maxCharacters { content = String(newValue.prefix(maxCharacters)) } } .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("完成") { isContentFieldFocused = false } .foregroundColor(.blue) .font(.system(size: 16, weight: .medium)) } } // 占位符文本 - 左上角对齐 if content.isEmpty && !isContentFieldFocused { VStack { HStack { Text(placeholder) .foregroundColor(.secondary) .font(.body) Spacer() } Spacer() } .padding(16) .allowsHitTesting(false) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) } } // 字符计数和限制提示 - 输入框底下 HStack { Spacer() // Pushes content to the right VStack(alignment: .trailing, spacing: 4) { // 字符限制提示 if content.count >= maxCharacters { HStack(spacing: 4) { Image(systemName: "exclamationmark.triangle") .font(.caption) .foregroundColor(.orange) Text("已达到最大字符数") .font(.caption) .foregroundColor(.orange) } } else if content.count >= Int(Double(maxCharacters) * 0.93) { HStack(spacing: 4) { Image(systemName: "info.circle") .font(.caption) .foregroundColor(.blue) Text("接近字符限制") .font(.caption) .foregroundColor(.blue) } } // 字符计数 Text("\(content.count)/\(maxCharacters)") .font(.caption) .foregroundColor(getCharacterCountColor()) } } } } private func getCharacterCountColor() -> Color { if content.count >= maxCharacters { return .orange } else if content.count >= Int(Double(maxCharacters) * 0.93) { return .blue } else { return .secondary } } } #Preview { TextInputView( content: .constant(""), placeholder: "输入任意文本内容...", maxCharacters: 150 ) }