SwiftUI: Is there any solution to clear value in DatePicker? - swift

I am finding a solution to clear value of a DatePicker in SwiftUI. I tried and it's not success. Please check:
struct EditProfileView: View {
#State var birthDate = Date()
var body: some View {
Form {
Section (header: Text("Birth day")) {
DatePicker(selection: $birthDate, in: ...Date(), displayedComponents: .date) {
Text("Select a date")
}
HStack {
Text("You were born in \(birthDate, formatter: dateFormatter)")
.font(.subheadline)
.foregroundColor(Color.gray)
Spacer()
Button(action: {
self.clearDate()
}) {
Text("Clear")
}
}
}
}
}
func clearDate () {
self.$birthDate = nil
}
}
This line of code is not work:
self.$birthDate = nil
I think because of Date type cant not leave nil but I cant find a solution to handle with it

Set the birthDate (not $birthDate) to the following, which will default back to the current date:
self.birthDate = Date()

You can create a Binding from Date?
struct EditProfileView: View {
#State var birthDate : Date?
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .short
return formatter
}
var body: some View {
Form {
Section (header: Text("Birth day")) {
DatePicker(selection: Binding<Date>(get: { () -> Date in
self.birthDate ?? Date()
}, set: { (Date) in
self.birthDate = Date
}), in: ...Date(), displayedComponents: .date) {
Text("Select a date")
}
HStack {
Text( "You were born in :") + (birthDate == nil ? Text("") : Text("\(birthDate!, formatter: dateFormatter)"))
.font(.subheadline)
.foregroundColor(Color.gray)
Spacer()
Button(action: {
self.clearDate()
}) {
Text("Clear")
}
}
}
}
}
func clearDate () {
self.birthDate = nil
}
}

Related

Is it possible to display saved DatePicker inputs to another view

I am trying to return DatePicker inputs which is saved to core data, from details view to another view. But nothing is being returned.
Where am I going wrong?
truct ProductCard: View {
#Environment (\.managedObjectContext) var managedObjContext
#FetchRequest(entity: Task.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Task.name, ascending: true)])
var body: some View {
ScrollView(.horizontal){
HStack{
ForEach( Task, id: \.self){ Task in
NavigationLink {
ProductCardDetails(taskData: Task)
} label: {
HStack{
Text(task.self.taskTitle ?? "Error")
.font(.title3)
.fontWeight(.bold)
.foregroundColor(.red)
Spacer()
// this should be Date right?
Text(task.self.taskDate ?? "Error")
.font(.caption)
.foregroundColor(.red)
}.padding(.horizontal, 10)
}
}
}
}
}
}
// where the inputs will be put in
struct ProductCardDetails: View {
let formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-dd-MM HH:mm:ss"
return formatter
}()
#Environment(\.managedObjectContext) var managedObjContext
#Environment(\.dismiss) var dismiss
var taskData: Task
#State private var taskTitle: String = ""
var body: some View {
let binding = Binding<Date> {
formatter.date(from: taskData.taskDate ?? "") ?? Date.now
} set: { date, transaction in
taskData.taskDate = formatter.string(from: date)
}
VStack{
Form{
Section(header: Text("Name the task"),
footer: Text("Footer of a section can contain extra information about this section")) {
TextField("Name your task", text: $taskTitle)
.disableAutocorrection(true)
}
Section(header: Text("When do you need to do this"),
footer: Text("")) {
DatePicker(selection: binding, in: ...Date()){
Text("Pick time")
}
}
Button("Save and close"){
Task{
do{
try await managedObjContext.perform {
try managedObjContext.save()
}
dismiss.callAsFunction()
} catch{
print(error)
}
}
}
}.navigationBarBackButtonHidden()
}
}
}
\
Added an image of my new core data and hopefully the naming conventions are clearer
\
core data
core data
and
current errors
[Entering in product details error][3]
Product card errors

Streak builder app using Swift and SwiftUI

I am looking to make a Streak builder app using Swift and SwiftUI. However, I am finding it difficult to create the logic for the counter using the Date(). Any suggestions will be highly appreciated.
Mainly I wanna replicate the Streak thing from the Duolingo app.
// I have this extension to follow up on the Date from the time user clicks it.
extension Date {
// for tomorow's Date
static var tomorrow: Date { return Date().dayAfter }
static var today: Date {return Date()}
var dayAfter: Date {
return Calendar.current.date(byAdding: .day, value: 1, to: Date())!
}
}
// More or less this was supposed to be my view.
struct StreakTrial: View {
#State var counter = 0
#State var TapDate: Date = Date.today
var body: some View {
NavigationView {
VStack{
Button {
// if TapDate != Date.today {
// counter += 1
// let TapDate = Date.tomorrow
// }
// else if TapDate == Date.tomorrow {
// counter = counter
// }
} label: {
Image(systemName: "flame")
.resizable()
.frame(width: 40, height: 50)
.padding()
.scaledToFit()
.background(Color.gray)
.foregroundColor(Color.orange)
.cornerRadius(12)
Text("\(counter)").foregroundColor(.gray)
}
Text("\(Date())")
.padding()
Text("\(Date().dayAfter)")
.padding()
}
}
}
}
**SO I TRIED SOME TUTORIALS OF #NICK SARNO WHICH GOES LIKE SWIFTFUL THINKING ON YOUTUBE AND GOT IT DONE**
*This code is compatible with Xcode 14.0*
import SwiftUI
extension Date {
// for tomorow's Date
static var tomorrow: Date { return Date().dayAfter }
static var today: Date {return Date()}
var dayAfter: Date {
return Calendar.current.date(byAdding: .day, value: 1, to: Date())!
// just add .minute after byAdding: , to create a streak minute counter and check the logic.
}
static func getTodayDate() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "E d MMM yyyy"
//to continue with the minute streak builder just add "E d MMM yyyy h:mm a" above, it will allow date formatting with minutes and follow the changes in dayAfter
return dateFormatter.string(from: Date.today)
}
static func getTomDate() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "E d MMM yyyy"
return dateFormatter.string(from: Date.tomorrow)
}
}
struct StreakApp: View {
#AppStorage("counter") var counter = 0
#AppStorage("tapDate") var TapDate: String?
#AppStorage("Tappable") var ButtonTapped = false
var body: some View {
NavigationView {
VStack{
VStack {
Text("\(counter)").foregroundColor(.gray)
Text("Restore your streak on ")
Text(TapDate ?? "No Date")
Image(systemName: "flame")
.resizable()
.frame(width: 40, height: 50)
.padding()
.scaledToFit()
.background(ButtonTapped ? Color.red : Color.gray)
.foregroundColor(ButtonTapped ? Color.orange : Color.black)
.cornerRadius(12)
}
Button {
if TapDate == nil {
//Check if user has already tapped
self.ButtonTapped = true
counter += 1
self.TapDate = ("\(Date.getTomDate())")
}
else if ("\(Date.getTodayDate())") == TapDate {
//Check for the consecutive Day of Streak
self.TapDate = ("\(Date.getTomDate())")
counter += 1
//Let's light the flame back again.
self.ButtonTapped = true
}
} label: {
RoundedRectangle(cornerRadius: 12, style: .continuous)
.foregroundColor(.black)
.frame(width: 120, height: 40)
.overlay {
Text("Add Streak")
.foregroundColor(.white)
}
}
.padding()
//This button is only for testing purpose.
Button {
self.TapDate = nil
self.ButtonTapped = false
self.counter = 0
} label: {
RoundedRectangle(cornerRadius: 12, style: .continuous)
.foregroundColor(.black)
.frame(width: 160, height: 40)
.overlay {
Text("Reset Streak")
.foregroundColor(.white)
}
}
}
//Ensuer the flame dies out if we run into any other day except today or tommorow.
.onAppear {
if ("\(Date.getTodayDate())") == TapDate ||
("\(Date.getTomDate())") == TapDate {
self.ButtonTapped = true
}
//Breaking the Streak
else {
self.TapDate = nil
self.ButtonTapped = false
self.counter = 0
}
}
}
}
}
struct StreakApp_Previews: PreviewProvider {
static var previews: some View {
StreakApp()
}
}

Dynamic list from TextField's data SwiftUI

I just started to learn Swift programing language and have a question.
I'm trying to create a simple one-page application where you can add movies to a favorite list. Movies must have 2 properties: title (string, mandatory) and year (integer, mandatory). But I have a problem, I don't know how to put it in one row.
And also, how to ignore duplicate movies?
import SwiftUI
struct Movie: Identifiable {
let id = UUID()
var title: String
}
class Model: ObservableObject {
#Published var movies: [Movie] = []
}
struct DynamicList: View {
#StateObject var model = Model()
#State var text = ""
#State var year = ""
var body: some View {
VStack {
Section() {
TextField("Title", text: $text)
.padding()
.border(.gray)
TextField("Year", text: $year)
.padding()
.border(.gray)
.keyboardType(.numberPad)
Button(action: {
self.addToList()
}, label: {
Text("Add")
.frame(width: 80, height: 40, alignment: .center)
.background(Color.blue)
.cornerRadius(8)
.foregroundColor(.white)
})
.padding()
}
List {
ForEach(model.movies) { movie in
MovieRow(title: movie.title)
}
}
}
.padding()
}
func addToList() {
guard !text.trimmingCharacters(in: .whitespaces).isEmpty else {
return
}
guard !year.trimmingCharacters(in: .whitespaces).isEmpty else {
return
}
let newMovie = Movie(title: text)
model.movies.append(newMovie)
text = ""
let newYear = Movie(title: year)
model.movies.append(newYear)
year = ""
}
}
struct MovieRow: View {
let title: String
var body: some View {
Label (
title: { Text(title)},
icon: { Image(systemName: "film") }
)
}
}
struct DynamicList_Previews: PreviewProvider {
static var previews: some View {
DynamicList()
}
}
Here is the solution. It will show the data in one row and also how to ignore duplicate movies to show into the list. Check the below code:
import SwiftUI
struct Movie: Identifiable {
var id = UUID()
let title: String
let year: String
}
class MoviesViewModel: ObservableObject {
#Published var movies: [Movie] = []
}
struct ContentView: View {
#State var boolValue = false
#StateObject var viewModel = MoviesViewModel()
#State var text = ""
#State var year = ""
var body: some View {
VStack {
Section() {
TextField("Title", text: $text)
.padding()
.border(.gray)
TextField("Year", text: $year)
.padding()
.border(.gray)
.keyboardType(.numberPad)
Button(action: {
self.addToList()
}, label: {
Text("Add")
.frame(width: 80, height: 40, alignment: .center)
.background(Color.blue)
.cornerRadius(8)
.foregroundColor(.white)
})
.padding()
}
// Show the data in list form
List {
ForEach(viewModel.movies) { movie in
MovieRow(title: movie.title, year: movie.year)
}
}
}
.padding()
}
func addToList() {
guard !text.trimmingCharacters(in: .whitespaces).isEmpty else {
return
}
guard !year.trimmingCharacters(in: .whitespaces).isEmpty else {
return
}
// Condition to check whether the data is already exit or not
boolValue = false
let newMovie = Movie(title: text, year: year)
for movie in viewModel.movies{
if ((movie.title.contains(text)) && (movie.year.contains(year))){
boolValue = true
}
}
// check if boolValue is false so the data will store into the array.
if boolValue == false{
viewModel.movies.append(newMovie)
text = ""
year = ""
}
}
}
struct MovieRow: View {
let title: String
let year: String
var body: some View {
// Show the data insert into the textfield
HStack{
Label (
title: { Text(title)},
icon: { Image(systemName: "film") }
)
Spacer()
Label (
title: { Text(year)},
icon: { Image(systemName: "calendar") }
)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Maybe someone will need a similar solution, here is my result:
import SwiftUI
struct Movie: Identifiable {
let id = UUID()
var title: String
var year: String
}
class Model: ObservableObject {
#Published var movies: [Movie] = []
}
struct DynamicList: View {
#StateObject var model = Model()
#State var text = ""
#State var year = ""
var body: some View {
VStack {
Section() {
TextField("Title", text: $text)
.padding()
.border(.gray)
TextField("Year", text: $year)
.padding()
.border(.gray)
.keyboardType(.numberPad)
Button(action: {
self.addToList()
}, label: {
Text("Add")
.frame(width: 80, height: 40, alignment: .center)
.background(Color.blue)
.cornerRadius(8)
.foregroundColor(.white)
})
.padding()
}
List {
ForEach(model.movies) { movie in
MovieRow(title: movie.title, year: movie.year)
}
}
}
.padding()
}
func addToList() {
guard !text.trimmingCharacters(in: .whitespaces).isEmpty else {
return
}
guard !year.trimmingCharacters(in: .whitespaces).isEmpty else {
return
}
let newMovie = Movie(title: text, year: year)
model.movies.append(newMovie)
text = ""
year = ""
}
}
struct MovieRow: View {
let title: String
let year: String
var body: some View {
Label (
title: { Text(title + " " + year)},
icon: { Image(systemName: "film") }
)
}
}
struct DynamicList_Previews: PreviewProvider {
static var previews: some View {
DynamicList()
}
}

Overlay a color over SwiftUI view?

For my SwiftUI notes app, I would like to overlay a color over the entire view, but I've only been able to cover half off the screen with the color. Other attempts at setting the color and an overlay has only resulted in errors so far. Is there a solution to this? Or should go about this another way? Image has been provided as well. 1
import SwiftUI
let dateFormatter2 = DateFormatter()
struct NoteItem2: Codable, Hashable, Identifiable {
let id: Int
let text: String
var date = Date()
var dateText: String {
dateFormatter.dateFormat = "h:mm a"
return dateFormatter.string(from: date)
}
}
struct green_screen : View {
#State var items: [NoteItem] = {
guard let data = UserDefaults.standard.data(forKey: "notes") else { return [] }
if let json = try? JSONDecoder().decode([NoteItem].self, from: data) {
return json
}
return []
}()
#State var taskText: String = ""
#State var showAlert = false
#State var itemToDelete: NoteItem?
var alert: Alert {
Alert(title: Text("Hey!"),
message: Text("Are you sure you want to delete this item?"),
primaryButton: .destructive(Text("Delete"), action: deleteNote),
secondaryButton: .cancel())
}
var inputView: some View {
Color.green
.overlay(
HStack {
TextField("Write a note for yourself...", text: $taskText)
.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
.clipped()
Button(action: didTapAddTask, label: { Text("Add") }).padding(8)
})
.edgesIgnoringSafeArea(.all)
}
var body: some View {
VStack {
inputView
Divider()
List(items) { item in
VStack(alignment: .leading) {
Text(item.dateText).font(.headline)
Text(item.text).lineLimit(nil).multilineTextAlignment(.leading)
}
.onLongPressGesture {
self.itemToDelete = item
self.showAlert = true
}
}
.alert(isPresented: $showAlert, content: {
alert
})
}
}
func didTapAddTask() {
let id = items.reduce(0) { max($0, $1.id) } + 1
items.insert(NoteItem(id: id, text: taskText), at: 0)
taskText = ""
save()
}
func deleteNote() {
guard let itemToDelete = itemToDelete else { return }
items = items.filter { $0 != itemToDelete }
save()
}
func save() {
guard let data = try? JSONEncoder().encode(items) else { return }
UserDefaults.standard.set(data, forKey: "notes")
}
}
struct green_screen_Previews: PreviewProvider {
static var previews: some View {
green_screen()
}
}
First you have to add green color to entire view instead of the inputView.
var inputView: some View {
GeometryReader{ geo in
HStack {
TextField("Write a note for yourself...", text: $taskText)
.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
.clipped()
Button(action: didTapAddTask, label: { Text("Add") }).padding(8)
}
.frame( height: geo.size.height/2)
}
}
and main view
VStack {
inputView
Divider()
List(items) { item in
VStack(alignment: .leading) {
Text(item.dateText).font(.headline)
Text(item.text).lineLimit(nil).multilineTextAlignment(.leading)
}
.onLongPressGesture {
self.itemToDelete = item
self.showAlert = true
}
}
.listRowBackground(Color.green)
.alert(isPresented: $showAlert, content: {
alert
})
}
.background(Color.green) // <-- here
.ignoresSafeArea() // <-- here
after this modification also you cannot see the color overlay on entire screen because of SwiftUI list block the view color from it's default background color.so you need to change it clear color,
init() {
UITableView.appearance().separatorStyle = .none
UITableViewCell.appearance().backgroundColor = .clear
UITableView.appearance().backgroundColor = .clear
}

Swiftui DatePicker events

I have a DatePicker such as:
DatePicker("DATE & TIME", selection: Binding(get: {
self.dateTime
}, set: { newValue in
self.dateTime = newValue
if newValue > Date() {
sendDateTimeToServer()
}
}), displayedComponents: [.date, .hourAndMinute])
As opposed to calling sendDateTimeToServer() every time dateTime changes I want to wait until the fullscreen (2nd image) DatePicker has collapsed, is there an event? Open to other suggestions too!
Thanks,
Update Property observers didSet gives a chance do some work when the popover is dismissed. Try this:
struct UpdateOnDismissView: View {
#EnvironmentObject var context : LaunchContext
var body: some View {
VStack {
Text("\(context.launch)").padding()
Button("Set Launch Date", action: { context.show.toggle() })
.padding()
.popover(isPresented: $context.show, content: { panel })
}
}
var panel : some View {
VStack {
Button("Done", action: { context.show.toggle() })
DatePicker("Launch", selection: Binding(get: {
context.launch
}, set: { newValue in
context.launch = newValue
}), displayedComponents: [.date, .hourAndMinute])
}
.padding()
.onDisappear(perform: {
print("Popover disappearing")
})
}
}
struct UpdateOnDismissView_Previews: PreviewProvider {
static var previews: some View {
UpdateOnDismissView().environmentObject(LaunchContext())
}
}
class LaunchContext : ObservableObject {
#Published var launch : Date = Date()
#Published var show : Bool = false { didSet {
if !show && launch < Date() {
sendLaunchToServer()
} else {
print("Dismissed")
}
}}
func sendLaunchToServer() {
print("Sending date \(launch)")
}
}
When should you use property observers?