Is there a way to format the date?(from the position indicated by the arrow in the picture) I know it is formatted based on the locale but is there a way to format it myself?
struct ContentView: View {
#State private var selectedDate = Date()
var body: some View {
Form {
DatePicker(selection: $selectedDate, in: ...Date(), displayedComponents: .date) {
Text("From*")
}
}
}
}
The only way I could figure to accomplish this is to create my own custom DatePicker view and use onAppear on the TextField to update an #State selectedDateText: String variable for displaying in the TextField. This feels like a hack and I’m almost embarrassed to post it but it works. I’m new at Swift and iOS programming in general so I’m sure someone will come along with a better answer so I’ll offer this for what it’s worth. My custom view is something like this:
struct CustomDatePicker: View {
#Binding var date: Date
#State private var showPicker: Bool = false
#State private var selectedDateText: String = "Date"
private var selectedDate: Binding<Date> {
Binding<Date>(get: { self.date}, set : {
self.date = $0
self.setDateString()
})
} // This private var I found… somewhere. I wish I could remember where
// To take the selected date and store it as a string for the text field
private func setDateString() {
let formatter = DateFormatter()
formatter.dateFormat = "MMMM dd, yyyy"
self.selectedDateText = formatter.string(from: self.date)
}
var body: some View {
VStack {
HStack {
Text("Date:")
.frame(alignment: .leading)
TextField("", text: $selectedDateText)
.onAppear() {
self.setDateString()
}
.disabled(true)
.onTapGesture {
self.showPicker.toggle()
}
.multilineTextAlignment(.trailing)
}
if showPicker {
DatePicker("", selection: selectedDate,
displayedComponents: .date)
.datePickerStyle(WheelDatePickerStyle())
.labelsHidden()
}
}
}
}
EDIT: I figured out where I got the private var code. It was from this post: How to detect a value change of a Datepicker using SwiftUI and Combine?
Related
I have a list with a .searchable search bar on top of it. It filters a struct that has text, but also a date.
I'm trying to have the user select whether he/she wants to search for all items containing the specific text, or search for all items with a data. I thought the way that iOS Mail did it when you hit he search bar on top is a good way (I'm open to other options tho...).
It looks like this:
So, when you tap the search field, the picker, or two buttons, or a tab selector shows up. I can't quite figure which is it. Regardless, I tried with a picker, but:
I don't know where to place it
I don't know how to keep it hidden until needed, and then hide it again.
this is the basic code:
let dateFormatter = DateFormatter()
struct LibItem: Codable, Hashable, Identifiable {
let id: UUID
var text: String
var date = Date()
var dateText: String {
dateFormatter.dateFormat = "EEEE, MMM d yyyy, h:mm a"
return dateFormatter.string(from: date)
}
var tags: [String] = []
}
final class DataModel: ObservableObject {
#AppStorage("myapp") public var collectables: [LibItem] = []
init() {
self.collectables = self.collectables.sorted(by: {
$0.date.compare($1.date) == .orderedDescending
})
}
func sortList() {
self.collectables = self.collectables.sorted(by: {
$0.date.compare($1.date) == .orderedDescending
})
}
}
struct ContentView: View {
#EnvironmentObject private var data: DataModel
#State var searchText: String = ""
var body: some View {
NavigationView {
List(filteredItems) { collectable in
VStack(alignment: .leading) {
Spacer() Text(collectable.dateText).font(.caption).fontWeight(.medium).foregroundColor(.secondary)
Spacer()
Text(collectable.text).font(.body).padding(.leading).padding(.bottom, 1)
Spacer()
}
}
.listStyle(.plain)
.searchable(
text: $searchText,
placement: .navigationBarDrawer(displayMode: .always),
prompt: "Search..."
)
}
}
var filteredItems: [LibItem] {
data.collectables.filter {
searchText.isEmpty ? true : $0.text.localizedCaseInsensitiveContains(searchText)
}
}
}
And I was trying to add something like, taking into account isSearching:
#Environment(\.isSearching) var isSearching
var searchBy = [0, 1] // 0 = by text, 1 = by date
#State private var selectedSearch = 0
// Yes, I'd add the correct text to it, but I wanted to have it
// working first.
Picker("Search by", selection: $selectedColor) {
ForEach(colors, id: \.self) {
Text($0)
}
}
How do I do it? How can I replicate that search UX from Mail? Or, is there any better way to let the user chose whether search text or date that appears when the user taps on the search?
isSearching works on a sub view that has a searchable modifier attached. So, something like this would work:
struct ContentView: View {
#EnvironmentObject private var data: DataModel
#State var searchText: String = ""
#State private var selectedItem = 0
var body: some View {
NavigationView {
VStack {
SearchableSubview(selectedItem: $selectedItem)
List(filteredItems) { collectable in
VStack(alignment: .leading) {
Spacer()
Text(collectable.dateText).font(.caption).fontWeight(.medium).foregroundColor(.secondary)
Spacer()
Text(collectable.text).font(.body).padding(.leading).padding(.bottom, 1)
Spacer()
}
}
.listStyle(.plain)
}.searchable(
text: $searchText,
placement: .navigationBarDrawer(displayMode: .always),
prompt: "Search..."
)
}
}
var filteredItems: [LibItem] {
data.collectables.filter {
searchText.isEmpty ? true : $0.text.localizedCaseInsensitiveContains(searchText)
}
}
}
struct SearchableSubview : View {
#Environment(\.isSearching) private var isSearching
#Binding var selectedItem : Int
var body: some View {
if isSearching {
Picker("Search by", selection: $selectedItem) {
Text("Choice 1").tag(0)
Text("Choice 2").tag(1)
}.pickerStyle(.segmented)
}
}
}
i got this struct
struct Calendar: View {
#State private var selectDate = Date()
var body: some View {
// my code
}
Class fetchmydate: ObservableObject {
func fetchmydata (){
// i want to pass selectDate to here in "dd-mm-yyyy" format as string
}
}
}
the idea i want to pass the selectdate from the view into observedobject class and not vice versa, but in this format "dd-mm-yyyy" and as a string. and if i don't pass this selected date it says it can't read the selected date inside the function.
i know it is somehow weird question but if it can't be done in logic (passing) then what do you suggest to pass the date data and thank you alot my friends.
What are you trying to achieve is not something unheard of, is just easier to pass the entire logic to the viewModel, I believe you are looking for this:
The view:
struct ViewDate: View {
#State private var date = Date()
var myviewModel = viewModel()
var body: some View {
DatePicker(selection: $date, displayedComponents: .date) {
Text("Select your date")
}
.datePickerStyle(.wheel)
.onChange(of: self.date) { newDate in
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
let stringDate = dateFormatter.string(from: newDate)
myviewModel.fetchmydata(selectedDate: stringDate)
}
}
}
The "View Model"
class viewModel: ObservableObject {
init() {}
func fetchmydata (selectedDate: String) {
// i want to pass selectDate to here in "dd-mm-yyyy" format as string
print(selectedDate)
}
}
I'm trying to fetch some data on a network call when a user selects a time from DatePicker.
I originally tested using .onChange but this fires every time that changes, and not on the final tap off.
DatePicker("Title", selection: $currentDate, displayedComponents: .hourAndMinute)
.onChange(of: $currentDate) { value in
print(value)
}
I also tried using the didset{} but that also fired on every change too.
#Published var currentDate: Date = Date() {
didSet { print(currentDate) }
}
What I want to do is once the user selects a time, I fire off some functions. I wanted to not do it every cycle in the wheel.
Is there a way to do this in SwiftUI or UIKit importing into SwiftUI?
Please see attached of what I'm looking at:
You could try rolling your own DatePickerView, such as this approach:
struct ContentView: View {
#State private var birthdate = Date()
var body: some View {
DatePickerView(date: $birthdate)
}
}
struct DatePickerView: View {
#Binding var date: Date
#State var hasChanged = false
var body: some View {
ZStack {
Color.white.opacity(0.01).ignoresSafeArea()
.onTapGesture {
if hasChanged {
print("---> do a network call now with: \(date)")
hasChanged = false
}
}
DatePicker("Birth Date", selection: $date, displayedComponents: .hourAndMinute)
.pickerStyle(.wheel)
.onChange(of: date) { val in
hasChanged = true
}
}
}
}
I want the user to select a date in the past by presenting a SwiftUI DatePicker. The date binding is set to today's date.
Now, I want the user to be able to select today's date. But when the user taps on the already selected date, the DatePicker is not dismissed. It only is dismissed when the user taps on a date that was not already selected. So in my case I want to dismiss the DatePicker when the user taps on 13.
Is there a way to dismiss the DatePicker when the user taps on the preselected date?
Here is how I use the DatePicker:
struct DatePickerView: View {
#Binding var date: Date
var body: some View {
DatePicker(
"Select Date",
selection: $date,
in: ...Date(),
displayedComponents: [.date]
)
.datePickerStyle(.graphical)
.background(Color.white)
.cornerRadius(8)
.padding(.horizontal, 40)
}
}
I had a similar problem and put a .graphical DatePicker in my own popover. The only downside is on iPhone popovers currently show as sheets which is an annoying inconsistency. Now you can control the popover presentation however you like and even have cancel/done actions.
struct DatePickerPopover: View {
#State var showingPicker = false
#State var oldDate = Date()
#Binding var date: Date
let doneAction: () -> ()
var body: some View {
Text(date, format:.dateTime.year())
.foregroundColor(.accentColor)
.onTapGesture {
showingPicker.toggle()
}
.popover(isPresented: $showingPicker, attachmentAnchor: .point(.center)) {
NavigationStack {
DatePicker(selection: $date
, displayedComponents: [.date]){
}
.datePickerStyle(.graphical)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") {
date = oldDate
showingPicker = false
}
}
ToolbarItem(placement: .confirmationAction) {
Button("Done") {
doneAction()
showingPicker = false
}
}
}
}
}
.onAppear {
oldDate = date
}
}
}
I got a simple SwiftUI view:
import SwiftUI
struct AddItemView: View {
#State private var title = ""
#State private var date = Date()
var body: some View {
Form {
Section {
TextField("Title", text: $title)
DatePicker(
selection: $date,
in: Date()...,
displayedComponents: .date,
label: { Text("Date") }
)
}
}
}
}
struct AddItemView_Previews: PreviewProvider {
static var previews: some View {
AddItemView()
}
}
I am trying to achieve the following:
If DatePicker is expanded (user tapped date picker, picker showing wheel to select date) and then starts typing text in TextField, DatePicker should automatically switch to initial, minimized mode (just showing label and selected date). Please take a look at screenshot. This is a behaviour in a stock Calendar.app, for example, when creating events.
Any help appreciated, thank you.
Here is possible approach. The idea is to reset DatePicker component for each of events result in editing.
Tested with Xcode 11.4 / iOS 13.4
struct AddItemView: View {
#State private var title = ""
#State private var date = Date()
#State private var pickerReset = UUID()
var body: some View {
Form {
Section {
TitleTextField()
DatePicker(
selection: $date,
in: Date()...,
displayedComponents: .date,
label: { Text("Date") }
).id(self.pickerReset)
}
}
}
private func TitleTextField() -> some View {
let boundText = Binding<String>(
get: { self.title },
set: { self.title = $0; self.pickerReset = UUID() }
)
return TextField("Title", text: boundText, onEditingChanged: { editing in
if editing {
self.pickerReset = UUID()
}
})
}
}