Last active
June 4, 2021 14:55
-
-
Save shial4/858585f486f2ce57d836bac2f469bcbe to your computer and use it in GitHub Desktop.
SwiftUI text field with date picker as keyboard accessory view and toolbar with done button
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// DateField.swift | |
// | |
// Created by Szymon Lorenz on 22/2/20. | |
// Copyright © 2020 Szymon Lorenz. All rights reserved. | |
// | |
import SwiftUI | |
struct DateTextField: UIViewRepresentable { | |
@Binding var date: Date | |
var didChange: () -> Void = { } | |
private var minimumDate: Date? = Date() | |
private var maximumDate: Date? = nil | |
private var placeholder: String? = "Select a date" | |
private var dateFormatter: DateFormatter = { | |
let formatter = DateFormatter() | |
formatter.dateStyle = .long | |
return formatter | |
}() | |
private var font: UIFont? | |
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 = UITextField() | |
textField.delegate = context.coordinator | |
textField.text = dateFormatter.string(from: date) | |
textField.font = font | |
textField.textColor = foregroundColor | |
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) | |
let datePickerView = UIDatePicker() | |
datePickerView.datePickerMode = .date | |
datePickerView.maximumDate = minimumDate | |
datePickerView.maximumDate = maximumDate | |
textField.inputView = datePickerView | |
datePickerView.addTarget(context.coordinator, action: #selector(Coordinator.handleDatePicker(sender:)), for: .valueChanged) | |
addDoneButtonToKeyboard(textField) | |
return textField | |
} | |
func updateUIView(_ uiView: UITextField, context: Context) { | |
uiView.text = dateFormatter.string(from: date) | |
} | |
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: "Done", 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, | |
didChange: didChange) | |
} | |
final class Coordinator: NSObject, UITextFieldDelegate { | |
@Binding var date: Date | |
var didChange: () -> Void | |
init(date: Binding<Date>, didChange: @escaping () -> Void) { | |
self._date = date | |
self.didChange = didChange | |
} | |
@objc func handleDatePicker(sender: UIDatePicker) { | |
date = sender.date | |
didChange() | |
} | |
func textFieldShouldReturn(_ textField: UITextField) -> Bool { | |
return false | |
} | |
} | |
} | |
extension DateTextField { | |
func minimumDate(_ date: Date?) -> some View { | |
var view = self | |
view.minimumDate = date | |
return view | |
} | |
func maximumDate(_ date: Date?) -> some View { | |
var view = self | |
view.maximumDate = date | |
return view | |
} | |
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 font(_ font: UIFont?) -> some View { | |
var view = self | |
view.font = font | |
return view | |
} | |
func foregroundColor(_ color: UIColor?) -> some View { | |
var view = self | |
view.foregroundColor = color | |
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 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment