Issue #590
Use custom NSTextField
as it is hard to customize TextFieldStyle
import SwiftUI
struct MaterialTextField: View {
let placeholder: String
@Binding var text: String
@State var isFocus: Bool = false
var body: some View {
VStack(alignment: .leading, spacing: 0) {
BorderlessTextField(placeholder: placeholder, text: $text, isFocus: $isFocus)
.frame(maxHeight: 40)
Rectangle()
.foregroundColor(isFocus ? R.color.separatorFocus : R.color.separator)
.frame(height: isFocus ? 2 : 1)
}
}
}
class FocusAwareTextField: NSTextField {
var onFocusChange: (Bool) -> Void = { _ in }
override func becomeFirstResponder() -> Bool {
let textView = window?.fieldEditor(true, for: nil) as? NSTextView
textView?.insertionPointColor = R.nsColor.action
onFocusChange(true)
return super.becomeFirstResponder()
}
}
struct BorderlessTextField: NSViewRepresentable {
let placeholder: String
@Binding var text: String
@Binding var isFocus: Bool
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeNSView(context: Context) -> NSTextField {
let textField = FocusAwareTextField()
textField.placeholderAttributedString = NSAttributedString(
string: placeholder,
attributes: [
NSAttributedString.Key.foregroundColor: R.nsColor.placeholder
]
)
textField.isBordered = false
textField.delegate = context.coordinator
textField.backgroundColor = NSColor.clear
textField.textColor = R.nsColor.text
textField.font = R.font.text
textField.focusRingType = .none
textField.onFocusChange = { isFocus in
self.isFocus = isFocus
}
return textField
}
func updateNSView(_ nsView: NSTextField, context: Context) {
nsView.stringValue = text
}
class Coordinator: NSObject, NSTextFieldDelegate {
let parent: BorderlessTextField
init(_ textField: BorderlessTextField) {
self.parent = textField
}
func controlTextDidEndEditing(_ obj: Notification) {
self.parent.isFocus = false
}
func controlTextDidChange(_ obj: Notification) {
guard let textField = obj.object as? NSTextField else { return }
self.parent.text = textField.stringValue
}
}
}