import SwiftUI
class CustomUITextField: UITextField {
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(UIResponderStandardEditActions.paste(_:)) || action == #selector(UIResponderStandardEditActions.copy(_:)) || action == #selector(UIResponderStandardEditActions.cut(_:)) {
return false
}
return super.canPerformAction(action, withSender: sender)
}
}
struct DateTextField: UIViewRepresentable {
@Binding var date: Date?
var didChange: () -> Void = {}
private var minimumYear: Int = 1990
private var maximumYear: Int = Calendar.current.component(.year, from: Date())
private var selectedYear: Int {
get {
return Calendar.current.component(.year, from: date ?? Date())
}
set {
let newDate = Calendar.current.date(bySetting: .year, value: newValue, of: date ?? Date())
if let newDate = newDate {
date = newDate
}
}
}
private var selectedMonth: Int {
get {
return Calendar.current.component(.month, from: date ?? Date())
}
set {
let newDate = Calendar.current.date(bySetting: .month, value: newValue, of: date ?? Date())
if let newDate = newDate {
date = newDate
}
}
}
private var placeholder: String? = ""
// private var dateFormatter: DateFormatter = {
// let formatter = DateFormatter()
// formatter.dateFormat = "yyyy년 MM월"
// return formatter
// }()
// private var foregroundColor: UIColor?
private var accentColor: UIColor?
private var textAlignment: NSTextAlignment?
private var contentType: UITextContentType?
private var autocorrection: UITextAutocorrectionType = .default
private var autocapitalization: UITextAutocapitalizationType = .sentences
private var keyboardType: UIKeyboardType = .default
private var returnKeyType: UIReturnKeyType = .default
private var isSecure: Bool = false
private var isUserInteractionEnabled: Bool = true
private var clearsOnBeginEditing: Bool = false
@Environment(\.layoutDirection) private var layoutDirection: LayoutDirection
init(date: Binding<Date?>, didChange: @escaping () -> Void = { }) {
self._date = date
self.didChange = didChange
}
func makeUIView(context: Context) -> UITextField {
let textField = CustomUITextField()
textField.delegate = context.coordinator
textField.placeholder = placeholder
textField.font = UIFont(name: "Pretendard-Medium", size: 18)
textField.textColor = UIColor(Color.theme.title)
if let textAlignment = textAlignment {
textField.textAlignment = textAlignment
}
if let contentType = contentType {
textField.textContentType = contentType
}
if let accentColor = accentColor {
textField.tintColor = accentColor
}
textField.autocorrectionType = autocorrection
textField.autocapitalizationType = autocapitalization
textField.keyboardType = keyboardType
textField.returnKeyType = returnKeyType
textField.clearsOnBeginEditing = clearsOnBeginEditing
textField.isSecureTextEntry = isSecure
textField.isUserInteractionEnabled = isUserInteractionEnabled
textField.setContentHuggingPriority(.defaultHigh, for: .vertical)
textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
textField.textAlignment = .center
textField.contentVerticalAlignment = .center
let pickerView = UIPickerView()
pickerView.dataSource = context.coordinator
pickerView.delegate = context.coordinator
pickerView.selectRow(selectedYear - minimumYear, inComponent: 0, animated: false)
pickerView.selectRow(selectedMonth - 1, inComponent: 1, animated: false)
textField.inputView = pickerView
addDoneButtonToKeyboard(textField)
return textField
}
func updateUIView(_ uiView: UITextField, context: Context) {
if let date = date {
uiView.text = date.yearAndMonthString()
} else {
uiView.text = ""
}
}
private func addDoneButtonToKeyboard(_ view: UITextField) {
let doneToolbar: UIToolbar = UIToolbar()
doneToolbar.barStyle = .default
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let done: UIBarButtonItem = UIBarButtonItem(title: "완료", style: .done, target: view, action: #selector(UITextField.resignFirstResponder))
done.setTitleTextAttributes([NSAttributedString.Key.font:UIFont.boldSystemFont(ofSize: 18)], for: .normal)
var items = [UIBarButtonItem]()
items.append(flexSpace)
items.append(done)
doneToolbar.items = items
doneToolbar.sizeToFit()
view.inputAccessoryView = doneToolbar
}
func makeCoordinator() -> Coordinator {
return Coordinator(date: $date, minYear: minimumYear, maxYear: maximumYear, didChange: didChange)
}
final class Coordinator: NSObject, UITextFieldDelegate, UIPickerViewDelegate, UIPickerViewDataSource {
@Binding var date: Date?
let minimumYear: Int
let maximumYear: Int
var didChange: () -> Void
init(date: Binding<Date?>, minYear: Int, maxYear: Int,didChange: @escaping () -> Void) {
self._date = date
self.minimumYear = minYear
self.maximumYear = maxYear
self.didChange = didChange
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 2
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if date == nil {
date = Date()
}
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == 0 { // Year component
return maximumYear - minimumYear + 1
} else if component == 1 { // Month component
let currentYear = Calendar.current.component(.year, from: Date())
if pickerView.selectedRow(inComponent: 0) + minimumYear == currentYear {
// If the selected year is the current year, limit the months to the current month
let currentMonth = Calendar.current.component(.month, from: Date())
return currentMonth
} else {
return 12
}
}
return 0
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
let currentYear = Calendar.current.component(.year, from: Date())
let currentMonth = Calendar.current.component(.month, from: Date())
if component == 0 { // Year component
let year = minimumYear + row
return (year <= currentYear) ? "\(year)년" : nil
} else if component == 1 { // Month component
if pickerView.selectedRow(inComponent: 0) + minimumYear == currentYear {
// If the selected year is the current year, filter out future months
return (row + 1 <= currentMonth) ? DateFormatter().monthSymbols[row] : nil
} else {
return DateFormatter().monthSymbols[row]
}
}
return nil
}
func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
return 120
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let currentYear = Calendar.current.component(.year, from: Date())
let currentMonth = Calendar.current.component(.month, from: Date())
if component == 0 { // Year component
let selectedYear = minimumYear + pickerView.selectedRow(inComponent: 0)
if selectedYear == currentYear {
// If the selected year is the current year, filter out future months
let currentMonth = Calendar.current.component(.month, from: Date())
pickerView.reloadComponent(1) // Reload the month component
pickerView.selectRow(min(currentMonth - 1, 11), inComponent: 1, animated: true)
} else {
pickerView.reloadComponent(1)
}
} else if component == 1 { // Month component
let selectedYear = minimumYear + pickerView.selectedRow(inComponent: 0)
let selectedMonth = pickerView.selectedRow(inComponent: 1) + 1
if selectedYear == currentYear && selectedMonth > currentMonth {
pickerView.selectRow(currentMonth - 1, inComponent: 1, animated: true)
}
}
updateDate(pickerView)
}
func updateDate(_ pickerView: UIPickerView) {
let selectedYear = minimumYear + pickerView.selectedRow(inComponent: 0)
let selectedMonth = pickerView.selectedRow(inComponent: 1) + 1
let calendar = Calendar.current
var components = DateComponents()
components.year = selectedYear
components.month = selectedMonth
if let newDate = calendar.date(from: components) {
date = newDate
didChange()
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
return false
}
}
}
extension DateTextField {
func placeholder(_ text: String?) -> some View {
var view = self
view.placeholder = text
return view
}
// func dateFormatter(_ formatter: DateFormatter) -> some View {
// var view = self
// view.dateFormatter = formatter
// return view
// }
func accentColor(_ accentColor: UIColor?) -> some View {
var view = self
view.accentColor = accentColor
return view
}
func multilineTextAlignment(_ alignment: TextAlignment) -> some View {
var view = self
switch alignment {
case .leading:
view.textAlignment = layoutDirection ~= .leftToRight ? .left : .right
case .trailing:
view.textAlignment = layoutDirection ~= .leftToRight ? .right: .left
case .center:
view.textAlignment = .center
}
return view
}
func textContentType(_ textContentType: UITextContentType?) -> some View {
var view = self
view.contentType = textContentType
return view
}
func disableAutocorrection(_ disable: Bool?) -> some View {
var view = self
if let disable = disable {
view.autocorrection = disable ? .no : .yes
} else {
view.autocorrection = .default
}
return view
}
func autocapitalization(_ style: UITextAutocapitalizationType) -> some View {
var view = self
view.autocapitalization = style
return view
}
func keyboardType(_ type: UIKeyboardType) -> some View {
var view = self
view.keyboardType = type
return view
}
func returnKeyType(_ type: UIReturnKeyType) -> some View {
var view = self
view.returnKeyType = type
return view
}
func isSecure(_ isSecure: Bool) -> some View {
var view = self
view.isSecure = isSecure
return view
}
func clearsOnBeginEditing(_ shouldClear: Bool) -> some View {
var view = self
view.clearsOnBeginEditing = shouldClear
return view
}
func disabled(_ disabled: Bool) -> some View {
var view = self
view.isUserInteractionEnabled = disabled
return view
}
}
Last active
October 19, 2023 02:53
-
-
Save woozoobro/efb001518be4c3534bf5c185d67fc8d5 to your computer and use it in GitHub Desktop.
SwiftUI에서 UIViewRepresentable을 이용해 TextField에 Picker로 값을 변경할 수 있게 해주는 컴포넌트 입니다.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment