SWIFTUI : get date only - swiftui

I'm trying to do something, and search, but I found nothing. I have a function DatePicker in a file. I call this function and send to it a binding var date. I want to keep only date and remove time from it. I have this function :
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
return formatter
}
But I use it like this : Text("(date, formatter: dateFormatter)")
How I can return the binding value with this formatter ?
thank you
edit my file :
struct WIDatePicker: View {
#Binding var date: Date
#State private var sheetDate = false
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
return formatter
}
var body: some View {
Button(action: {self.sheetDate.toggle()}) {
VStack {
HStack {
Text("\(date, formatter: dateFormatter)")
Print("Variables : \(dateFormatter.string(from: date))")
Spacer()
if (sheetDate == true) {
Divider()
DatePicker("", selection: $date, displayedComponents: .date)
.labelsHidden()
}
}
}
}
}

I guess you are asking how to use the formatter. Use it like this:
Text("date \(date, formatter: dateFormatter)")

maybe you could do something like this, to get the string date without hours and minutes.
struct WIDatePicker: View {
#State var date: Date
#State private var sheetDate = false
#State var myDateString = ""
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
return formatter
}
var body: some View {
let bindingDate = Binding<Date>(
get: { self.date },
set: {
self.myDateString = self.dateFormatter.string(from: $0)
self.date = $0
})
return Button(action: {
self.sheetDate.toggle()
}) {
VStack {
HStack {
Text("\(self.myDateString)")
Spacer()
if (sheetDate == true) {
Divider()
DatePicker("", selection: bindingDate, displayedComponents: .date).labelsHidden()
}
}
}
}.onAppear(perform: loadData)
}
func loadData() {
myDateString = dateFormatter.string(from: date)
}
}

Related

how to convert muliple int number to string in qr code generator with swiftui

I have made a qr code generator with date and I want to add another picker for hours, and I have tried to conver Int to String, there isn't any problem, when I tried to convert multiple Int to string and it doesn't work and also i want to change the Int format to something like this 12 02:11, first is integer space and time. how could I do that?
My Code:
struct GenerateQRCode: View {
#Binding var time: Date
#Binding var hours: Int
let hour = ["3","6","9","12"]
let filter = CIFilter.qrCodeGenerator()
let cont = CIContext()
var dateFormatter: DateFormatter {
let df = DateFormatter()
df.dateFormat = "HH:mm"
return df
}
var body: some View {
NavigationView{
Image(uiImage: imageGenerate(times:time, hours: hours))
.interpolation(.none)
.resizable()
.frame(width: 150, height: 150, alignment: .center)
}.navigationBarBackButtonHidden(true)
}
func imageGenerate(hours: Int, times: Date)-> UIImage { //<--here how to add integer parameter?
let str = dateFormatter.string(from: start)
let ts = String(hours)
let com = ts + str
let data = com.data(using: .utf8)
filter.setValue(data, forKey: "inputMessage") //
if let qr = filter.outputImage {
if let qrImage = cont.createCGImage(qr, from: qr.extent){
return UIImage(cgImage: qrImage)
}
}
return UIImage(systemName: "xmark") ?? UIImage()
}
}
Preview:
import Foundation
import SwiftUI
import CoreImage.CIFilterBuiltins
struct DatePicker: View {
#State var Time = Date()
#State var sHours = Int()
#State var navigated = false
let hour = ["3", "6", "9", "12"]
var body: some View {
NavigationView{
VStack{
Section{
Text("Please Select Time")
DatePicker("", selection: $startTime, displayedComponents: [.hourAndMinute])
.datePickerStyle(.wheel)
}
Section{
Text("Please Select Minutes")
Picker(selection: $sMinutes, label: Text("Please Select Minutes"))
{
ForEach(0 ..< minutes.count) {
index in Text(self.minutes[index]).tag(index)
}
}
}
Section
{
NavigationLink(destination: GenerateQRCode(start: $Time, minutes: $sHours), isActive: self.$navigated)
{
Text("Complete")
}
}.padding(100)
}.navigationBarTitle("Visitor")
}
}
}
struct DatePicker_Previews: PreviewProvider {
static var previews: some View {
DatePicker()
}
}
Output: "012:30"
what i expected output, should be like this: "3 12:30" first should be hours and then time. i don't know why it only shows 0 if my picker turns to 3. How can i solve it out?

Initializing the Date Picker with Date Other Than Today

I need help with dates. How do you initialize the DatePicker with a stored date? Say, for example, that the user entered an entry with a date. He then determines the date is wrong and would like to change the date. But currently the DatePicker in this code will always default to today's date instead of the stored date.
The state parameter startDate can't be initialized with a stored date above the body. It appears that I need to set startDate in the body.
import SwiftUI
import Combine
struct GetDate: View {
#ObservedObject var userData : UserData = UserData()
#State private var startDate = Date()
var body: some View {
//let startDate = userData.startDate
VStack (alignment: .leading) {
Form {
Text("startdate 1 = \(startDate)")
// enter start date
Section(header: Text("Enter Start Date")) {
HStack {
Image(systemName: "calendar.badge.clock")
.resizable()
.foregroundColor(Color(.systemRed))
.frame(width: 35, height: 35)
Spacer()
DatePicker("", selection: $startDate, in: ...Date(), displayedComponents: .date)
.datePickerStyle(CompactDatePickerStyle())
}
}
Button ( action: {
userData.saveDates(startDate: startDate)
}) {
HStack {
Spacer()
Text("Save Dates?")
Spacer()
}
}
}
.font(.body)
.navigationBarTitle("Get Date", displayMode: .inline)
}
}
}
class UserData: ObservableObject {
#Published var startDate: Date = Date() {
didSet {
UserDefaults.standard.set(startDate, forKey: "startDate") // save
}
}
init() {
// save / retrieve trip dates
if let sdate = UserDefaults.standard.object(forKey: "startDate") as? Date {
startDate = sdate
print("startDate 2 = \(startDate)")
} else {
UserDefaults.standard.set(Date(), forKey: "startDate")
}
}
func saveDates(startDate: Date) -> () {
self.startDate = startDate
UserDefaults.standard.set(self.startDate, forKey: "startDate")
print("startDate 3 = \(self.startDate)")
}
}
Use #AppStorage property wrapper:
struct GetDate: View {
#AppStorage("startDate") var startDate = Date()
#State private var selectedDate = Date()
var body: some View {
VStack (alignment: .leading) {
Form {
Text("startdate 1 = \(startDate)")
Section(header: Text("Enter Start Date")) {
HStack {
Image(systemName: "calendar.badge.clock")
.resizable()
.foregroundColor(Color(.systemRed))
.frame(width: 35, height: 35)
Spacer()
DatePicker("", selection: $selectedDate, in: ...Date(), displayedComponents: .date)
.datePickerStyle(CompactDatePickerStyle())
}
}
Button ( action: {
startDate = selectedDate // save
}) {
HStack {
Spacer()
Text("Save Dates?")
Spacer()
}
}
}
.font(.body)
.navigationBarTitle("Get Date", displayMode: .inline)
}
.onAppear {
selectedDate = startDate
}
}
}
// Support #AppStorage for `Date` type.
extension Date: RawRepresentable {
private static let formatter = ISO8601DateFormatter()
public var rawValue: String {
Date.formatter.string(from: self)
}
public init?(rawValue: String) {
self = Date.formatter.date(from: rawValue) ?? Date()
}
}
Just reset startDate to userData.startDate in .onAppear().
.onAppear {
startDate = userData.startDate
}

Published/Observed var not updating in view swiftui w/ called function

Struggling to get a simple example up and running in swiftui:
Load default list view (working)
click button that launches picker/filtering options (working)
select options, then click button to dismiss and call function with selected options (call is working)
display new list of objects returned from call (not working)
I'm stuck on #4 where the returned query isn't making it to the view. I suspect I'm creating a different instance when making the call in step #3 but it's not making sense to me where/how/why that matters.
I tried to simplify the code some, but it's still a bit, sorry for that.
Appreciate any help!
Main View with HStack and button to filter with:
import SwiftUI
import FirebaseFirestore
struct TestView: View {
#ObservedObject var query = Query()
#State var showMonPicker = false
#State var monFilter = "filter"
var body: some View {
VStack {
HStack(alignment: .center) {
Text("Monday")
Spacer()
Button(action: {
self.showMonPicker.toggle()
}, label: {
Text("\(monFilter)")
})
}
.padding()
ScrollView(.horizontal) {
LazyHStack(spacing: 35) {
ForEach(query.queriedList) { menuItems in
MenuItemView(menuItem: menuItems)
}
}
}
}
.sheet(isPresented: $showMonPicker, onDismiss: {
//optional function when picker dismissed
}, content: {
CuisineTypePicker(selectedCuisineType: $monFilter)
})
}
}
The Query() file that calls a base query with all results, and optional function to return specific results:
import Foundation
import FirebaseFirestore
class Query: ObservableObject {
#Published var queriedList: [MenuItem] = []
init() {
baseQuery()
}
func baseQuery() {
let queryRef = Firestore.firestore().collection("menuItems").limit(to: 50)
queryRef
.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
self.queriedList = querySnapshot?.documents.compactMap { document in
try? document.data(as: MenuItem.self)
} ?? []
}
}
}
func filteredQuery(category: String?, glutenFree: Bool?) {
var filtered = Firestore.firestore().collection("menuItems").limit(to: 50)
// Sorting and Filtering Data
if let category = category, !category.isEmpty {
filtered = filtered.whereField("cuisineType", isEqualTo: category)
}
if let glutenFree = glutenFree, !glutenFree {
filtered = filtered.whereField("glutenFree", isEqualTo: true)
}
filtered
.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
self.queriedList = querySnapshot?.documents.compactMap { document in
try? document.data(as: MenuItem.self);
} ?? []
print(self.queriedList.count)
}
}
}
}
Picker view where I'm calling the filtered query:
import SwiftUI
struct CuisineTypePicker: View {
#State private var cuisineTypes = ["filter", "American", "Chinese", "French"]
#Environment(\.presentationMode) var presentationMode
#Binding var selectedCuisineType: String
#State var gfSelected = false
let query = Query()
var body: some View {
VStack(alignment: .center) {
//Buttons and formatting code removed to simplify..
}
.padding(.top)
Picker("", selection: $selectedCuisineType) {
ForEach(cuisineTypes, id: \.self) {
Text($0)
}
}
Spacer()
Button(action: {
self.query.filteredQuery(category: selectedCuisineType, glutenFree: gfSelected)
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text( "apply filters")
})
}
.padding()
}
}
I suspect that the issue stems from the fact that you aren't sharing the same instance of Query between your TestView and your CuisineTypePicker. So, when you start a new Firebase query on the instance contained in CuisineTypePicker, the results are never reflected in the main view.
Here's an example of how to solve that (with the Firebase code replaced with some non-asynchronous sample code for now):
struct MenuItem : Identifiable {
var id = UUID()
var cuisineType : String
var title : String
var glutenFree : Bool
}
struct ContentView: View {
#ObservedObject var query = Query()
#State var showMonPicker = false
#State var monFilter = "filter"
var body: some View {
VStack {
HStack(alignment: .center) {
Text("Monday")
Spacer()
Button(action: {
self.showMonPicker.toggle()
}, label: {
Text("\(monFilter)")
})
}
.padding()
ScrollView(.horizontal) {
LazyHStack(spacing: 35) {
ForEach(query.queriedList) { menuItem in
Text("\(menuItem.title) - \(menuItem.cuisineType)")
}
}
}
}
.sheet(isPresented: $showMonPicker, onDismiss: {
//optional function when picker dismissed
}, content: {
CuisineTypePicker(query: query, selectedCuisineType: $monFilter)
})
}
}
class Query: ObservableObject {
#Published var queriedList: [MenuItem] = []
private let allItems: [MenuItem] = [.init(cuisineType: "American", title: "Hamburger", glutenFree: false),.init(cuisineType: "Chinese", title: "Fried Rice", glutenFree: true)]
init() {
baseQuery()
}
func baseQuery() {
self.queriedList = allItems
}
func filteredQuery(category: String?, glutenFree: Bool?) {
queriedList = allItems.filter({ item in
if let category = category {
return item.cuisineType == category
} else {
return true
}
}).filter({item in
if let glutenFree = glutenFree {
return item.glutenFree == glutenFree
} else {
return true
}
})
}
}
struct CuisineTypePicker: View {
#ObservedObject var query : Query
#Binding var selectedCuisineType: String
#State private var gfSelected = false
private let cuisineTypes = ["filter", "American", "Chinese", "French"]
#Environment(\.presentationMode) private var presentationMode
var body: some View {
VStack(alignment: .center) {
//Buttons and formatting code removed to simplify..
}
.padding(.top)
Picker("", selection: $selectedCuisineType) {
ForEach(cuisineTypes, id: \.self) {
Text($0)
}
}
Spacer()
Button(action: {
self.query.filteredQuery(category: selectedCuisineType, glutenFree: gfSelected)
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text( "apply filters")
})
}
}

In SwiftUI how can I use #State and #Binding to deal with different types?

As the title might not very clear, below is an example:
I have a View which is just DatePicker, name is "MIMIRxDatePicker"
struct MIMIRxDatePicker: View {
#Binding var dobStr: String
#Binding var screenShouldGrayOut: Bool
var body: some View {
VStack {
Text("Select Date of Birth: \(dateFormatter.string(from: self.dobStr))")
DatePicker(selection: self.dobStr , in: ...Date(), displayedComponents: .date) {
Text("")
}
Button(action: {
withAnimation {
self.screenShouldGrayOut.toggle()
}
}) {
Text("Choose").foregroundColor(.white).padding()
}.background(Color(Constants.ThemeColor)).cornerRadius(20)
}.cornerRadius(10).frame(width: 270).padding(20).background(Color(.white))
}
}
Heres is MIMIRxDatePicker s parent view: SignupContentView
struct SignupContentView: View {
#State var email: String = ""
#State var firstName: String = ""
#State var lastName: String = ""
#State var dobStr: String = ""
#State var screenShouldGrayOut: Bool = false
var body: some View {
ZStack {
Color(Constants.ThemeColor)
ScrollView(.vertical) {
Spacer().frame(height: 50)
VStack (alignment: .center, spacing: 2) {
Spacer()
Group {
Group {
HStack {
Text("Email:").modifier(AuthTextLabelModifier())
Spacer()
}.padding(2)
HStack {
Image(systemName: "envelope").foregroundColor(.white)
TextField("Email", text: $email).foregroundColor(.white)
}.modifier(AuthTextFieldContainerModifier())
}
Group {
HStack {
Text("First Name:").modifier(AuthTextLabelModifier())
Spacer()
}.padding(2)
HStack {
Image(systemName: "person").foregroundColor(.white)
TextField("First Name", text: $firstName).foregroundColor(.white)
}.modifier(AuthTextFieldContainerModifier())
}
Group {
HStack {
Text("Last Name:").modifier(AuthTextLabelModifier())
Spacer()
}.padding(2)
HStack {
Image(systemName: "person").foregroundColor(.white)
TextField("Last Name", text: $lastName).foregroundColor(.white)
}.modifier(AuthTextFieldContainerModifier())
Spacer()
HStack { GenderSelector() }
}
Group {
HStack {
Text("Date of Birth:").modifier(AuthTextLabelModifier())
Spacer()
}.padding(2)
HStack {
Image(systemName: "calendar").foregroundColor(.white)
TextField("", text: $dobStr).foregroundColor(.white).onTapGesture {
withAnimation {
self.screenShouldGrayOut.toggle()
}
}
}.modifier(AuthTextFieldContainerModifier())
}
}
}.padding()
}.background(screenShouldGrayOut ? Color(.black) : Color(.clear)).navigationBarTitle("Sign Up", displayMode: .inline)
if screenShouldGrayOut {
MIMIRxDatePicker(dobStr: $dobStr, screenShouldGrayOut: $screenShouldGrayOut).animation(.spring())
}
}.edgesIgnoringSafeArea(.all)
}
}
As you can see the #State var dobStr in "SignupContentView" is a String which is Date of birth's TextField needed, but in the MIMIRxDatePicker the DatePicker's selection param needs a type of Date, not a string, even if I declared a #Binding var dobStr: String in MIMIRxDatePicker.
I also tried:
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .long
return formatter
}
and do:
DatePicker(selection: dateFormatter.date(from: self.dobStr) , in: ...Date()....
But it didn't work.
I know that assume if the DatePicker is a TextField then everything will work and good to go because TextField accept String only also, but that's not the case, I need the DatePicker.
So in SwiftUI how can I use #State and #Binding to deal with different types?
What you can do is to use Date for date manipulation and String for displaying the date.
Which means you can use Date variable in your Picker and String in the Text views.
struct MIMIRxDatePicker: View {
#State var dob: Date
...
let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .long
return formatter
}()
var dobStr: String {
dateFormatter.string(from: self.dob)
}
var body: some View {
VStack {
// `String` for displaying...
Text("Select Date of Birth: \(self.dobStr)")
// and `Date` for date manipulation...
DatePicker(selection: self.$dob, in: ...Date(), displayedComponents: .date) {
Text("")
}
...
}
...
}
}
Then follow the same pattern in your SignupContentView. Generally try to use Date objects for date manipulation as they are less prone to errors and malformed data.
You need to first identify the types involved.
You are passing a Binding<String> to selection: which expects a Binding<Date>.
#Binding var dobStr: String is still Binding<String>
dateFormatter.date(from: self.dobStr) is Date, not Binding<Date>
What you need is to create a custom Binding<Date>, with its getter/setter interacting with Binding<String>
E.g.;
Binding(
get: { // return a Date from dobStr binding },
set: { // set dobStr binding from a Date}
)
Then use this Binding as argument to selection:

How to set the style of the textField part of a DatePicker

I used a DatePicker inside a Form, and It looks like the following image ,
Now,I hope it display "2020-4-19 " instead of "4/19/20 ".
Someone knows how to do it?
I could not find a way with the default DatePicker, so I've taken the code from
Change selected date format from DatePicker SwiftUI
made some changes to make it work. This CustomDatePicker should do you what you asked for even within a Form.
struct CustomDatePicker: View {
#State var text: String = "Date"
#Binding var date: Date
#State var formatString: String = "yyyy-MM-dd"
#State private var disble: Bool = false
#State private var showPicker: Bool = false
#State private var selectedDateText: String = "Date"
let formatter = DateFormatter()
private func setDateString() {
formatter.dateFormat = formatString
self.selectedDateText = formatter.string(from: self.date)
}
var body: some View {
VStack {
HStack {
Text(text).frame(alignment: .leading)
Spacer()
Text(self.selectedDateText)
.onAppear() {
self.setDateString()
}
.foregroundColor(.blue)
.onTapGesture {
self.showPicker.toggle()
}.multilineTextAlignment(.trailing)
}
if showPicker {
DatePicker("", selection: Binding<Date>(
get: { self.date},
set : {
self.date = $0
self.setDateString()
}), displayedComponents: .date)
.datePickerStyle(WheelDatePickerStyle())
.labelsHidden()
}
}
}
}
struct ContentView: View {
#State var date = Date()
var body: some View {
Form {
Section {
CustomDatePicker(text: "my date", date: $date, formatString: "yyyy-MM-dd")
Text("test")
}
}
}