Calculating Age From Date Picker Swift 3 - ios

In my code below i have implemented a date picker to be the subview of my textfield. I am stuck on the function; 'verifyAge'. The if statement in the function is working fine and will change the textField colour when the Date Picker date is above or below the specified date. But it only works to the corresponding year But does not Work to the exact Day. I have tried searching for the past two days and couldn't find a solution to my exact problem.
class AgeConfirmViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var txtAgeConfirmation: UITextField!
let datePicker: UIDatePicker = UIDatePicker()
override func viewDidLoad() {
super.viewDidLoad()
txtAgeConfirmation.delegate = self
createDatePicker()
}
func textFieldDidBeginEditing(_ textField: UITextField) {
txtAgeConfirmation.inputView = datePicker
datePicker.addTarget(self, action: #selector(self.datePickerChanged(sender:)), for: .valueChanged)
}
func datePickerChanged(sender: UIDatePicker) {
let formatter = DateFormatter()
formatter.timeStyle = .none
formatter.dateStyle = .long
txtAgeConfirmation.text = formatter.string(from: sender.date)
verifyAge()
}
func createDatePicker() {
datePicker.datePickerMode = .date
datePicker.maximumDate = Calendar.current.date(byAdding: .year, value: 0, to: Date())
}
func verifyAge() {
let dateOfBirth = datePicker.date
let today = Date()
let gregorian = NSCalendar(identifier: NSCalendar.Identifier.gregorian)
let age = gregorian?.components([.month, .day, .year], from: dateOfBirth, to: today, options:[])
if (age?.year)! < 21 {
txtAgeConfirmation.textColor = UIColor.red
} else if (age?.year)! > 21 {
txtAgeConfirmation.textColor = UIColor.black
}
}

Simple solution, ignore day and month:
let gregorian = Calendar(identifier: .gregorian)
let ageComponents = gregorian.dateComponents([.year], from: dateOfBirth, to: Date())
let age = ageComponents.year!
txtAgeConfirmation.textColor = age < 21 ? .red : .black
Or if you want to ignore the time
let ageComponents = calendar.dateComponents([.year], from: calendar.startOfDay(for: dateOfBirth), to: calendar.startOfDay(for: Date()))
In Swift 3 there are less question and exclamation marks by using native Calendar struct.
The age exclamation mark after year is absolutely safe because the year component is clearly specified.

Related

Calendar Hour component is 5 hours ahead, Unable to adjust it

I have two components in my app. A UIDATEPICKER that is set to select the time only, HOURS AND MINUTES. and A calendar (FSCALENDAR 3rd party) that selects the date,month and year for me. I am trying to combine the time selected from a uidatepicker and the date selected from the calendar to create a notification. it is working so far but the hour is 5 hours ahead. i think it is because of the 12 hour format of the uidatepicker. how do i extract the hour component in 24 hours format?
The code below is for extracting the date and time.
class ReminderViewController: UIViewController, FSCalendarDelegate, FSCalendarDataSource {
var reminderDate : String?
var reminderDay : Int?
var reminderMonth : Int?
var reminderYear : Int?
var reminderHour : Int?
var reminderMinute : Int?
#IBOutlet weak var rDatePicker: UIDatePicker!
#IBAction func reminderDatePicker(_ sender: Any) {
var df = DateFormatter()
let date = rDatePicker.date
let dateAsString = df.string(from: date)
let components = Calendar.current.dateComponents([.hour, .minute], from: date)
reminderHour = components.hour!
reminderMinute = components.minute!
}
#IBOutlet weak var doneButton: UIButton!
#IBAction func doneAction(_ sender: Any) {
let calendar = Calendar.current
let totalComponents = NSDateComponents()
totalComponents.day = reminderDay!
totalComponents.hour = reminderHour!
totalComponents.month = reminderMonth!
totalComponents.minute = reminderMinute!
totalComponents.year = reminderYear!
let finalDate = calendar.date(from: totalComponents as DateComponents)
print(finalDate)
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.widthAnchor.constraint(equalToConstant: 500).isActive = true
reminderCalendar.delegate = self
reminderCalendar.dataSource = self
let totalComponents = NSDateComponents()
let calendar = Calendar.current
let date = Date()
let components = Calendar.current.dateComponents([.hour, .minute, .month, .day , .year], from: date)
reminderHour = components.hour!
reminderMinute = components.minute!
reminderMonth = components.month!
reminderDay = components.day!
reminderYear = components.year!
let finalDate = calendar.date(from: components as DateComponents)
print("FINAL" , finalDate)
}
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
print(date)
var df = DateFormatter()
df = DateFormatter()
df.dateFormat = "dd-MM-yyyy"
reminderDate = df.string(from: date)
reminderMonth = Calendar.current.component(.month, from: date)
reminderDay = Calendar.current.component(.day, from: date)
reminderYear = Calendar.current.component(.year, from: date)
print(reminderMonth)
print(reminderYear)
print(reminderDay)
}
}
Get a date and time and Convert it in UST format based on your timezone you are ahead that's why it create issue
func localToUTC(date:String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
dateFormatter.calendar = NSCalendar.current
dateFormatter.timeZone = TimeZone.current
let dt = dateFormatter.date(from: date)
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
dateFormatter.dateFormat = "yyyy-MM-dd"
return dateFormatter.string(from: dt!)
}
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
print(date)
var df = DateFormatter()
df = DateFormatter()
df.dateFormat = "yyyy:MM:dd:HH:mm:ss"
reminderDate = df.string(from: date)
reminderMonth = Calendar.current.component(.month, from: date)
reminderDay = Calendar.current.component(.day, from: date)
reminderYear = Calendar.current.component(.year, from: date)
print(reminderMonth)
print(reminderYear)
print(reminderDay)
}

How do I print the age based on the selected date picker on a text field?

When I select the date the app stops responding. I want to select the date and show it in a textfield without pressing a done button. So I want to see date in live.
How do I print the age based on the selected Date picker on a TextField?
import UIKit
import Firebase
class SignUpEmailSegue0: UIViewController {
#IBOutlet weak var dateBirthday: UITextField!
#IBAction func dateBirthdayEdit(_ sender: UITextField) {
let datePickerView = UIDatePicker()
datePickerView.datePickerMode = UIDatePickerMode.date
sender.inputView = datePickerView
datePickerView.addTarget(self, action: #selector(handleDatePicker), for: UIControlEvents.valueChanged)
}
#objc func handleDatePicker(sender: UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MMM/yyyy"
dateBirthday.text = dateFormatter.string(from: sender.date)
}
}
Try changing your code as follows - using #selector() and exposing your function to #objc
datePickerView.addTarget(self, action: #selector(handleDatePicker), for: UIControlEvents.valueChanged)
#objc func handleDatePicker(sender: UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
dateBirthday.text = dateFormatter.string(from: sender.date)
}
To answer your second question of how to get the age:
let gregorian = Calendar(identifier: .gregorian)
let ageComponents = gregorian.dateComponents([.year], from: sender.date, to: Date())
if let age = ageComponents.year {
print(age)
}

Can't Set a Converted Date from String as Minimum Date in DatePicker

I have two datepickers that contains time with format 7:00 AM and 8:00 PM for login and logout. These times may vary. I wanted to change the logout time and tried to use the login time as the minimum time but my code does not work, I can still select time like 1:00 AM or 6:00 AM as logout.
This is the code in my ViewDidLoad
if let actualLoginText = loginTextField.text {
let start = actualLoginText
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
formattedTime = formatter.date(from: start)!
}
And this is the code of my logout picker
#objc func timeOutPickerValueChanged(sender: UIDatePicker) {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
timeOutPicker.minimumDate = Calendar.current.date(byAdding: .minute, value: 5, to: formattedTime)
logoutTextField.text = formatter.string(from: sender.date)
}
I also tried this but still doesn't work
if let actualLoginText = loginTextField.text {
let start = actualLoginText
let formatter = DateFormatter()
formatter.dateFormat = "hh:mm a"
formattedTime = formatter.date(from: start)!
}
Thank you.
When working with dates / times, work with Date objects, not strings. The "string representation" should only be used to display the date/time to the user, not when manipulating the values.
Here is a simple example (assuming you have added two UIDatePickers and connected their Value Changed events). On load, it initializes the timeInPicker to the current date/time. Any time you select a new "Time In", the timeOutPicker.minimumDate is set to 5-minutes from the selected time, and the timeOutPicker.maximumDate is set to 8-hours from the minimum time:
class ViewController: UIViewController {
#IBOutlet weak var timeInPicker: UIDatePicker!
#IBOutlet weak var timeOutPicker: UIDatePicker!
let dateFormatter: DateFormatter = {
let df = DateFormatter()
df.dateStyle = .short
df.timeStyle = .short
return df
}()
override func viewDidLoad() {
super.viewDidLoad()
// Initialize the TimeIn and TimeOut pickers to the current Date/Time
let now = Date()
updateTimeOutPicker(now)
}
func updateTimeOutPicker(_ withStartDateTime: Date) -> Void {
// add 5 minutes to the selected "In" time to get the "minimum time" for the Out Picker
guard let minDateTime = Calendar(identifier: .gregorian).date(byAdding: .minute, value: 5, to: withStartDateTime) else { return }
// add 8 hours to the to get the "maximum time" for the Out Picker
guard let maxDateTime = Calendar(identifier: .gregorian).date(byAdding: .hour, value: 8, to: minDateTime) else { return }
timeOutPicker.minimumDate = minDateTime
timeOutPicker.maximumDate = maxDateTime
print("New Min Out Time:", dateFormatter.string(from: minDateTime))
print("New Max Out Time:", dateFormatter.string(from: maxDateTime))
print()
}
#IBAction func timeInPickerValueChanged(_ sender: UIDatePicker) {
print("Selected In Time:", dateFormatter.string(from: sender.date))
print()
updateTimeOutPicker(sender.date)
}
#IBAction func timeOutPickerValueChanged(_ sender: UIDatePicker) {
print("Selected Out Time:", dateFormatter.string(from: sender.date))
print()
}
}
The login and logout time strings were pulled up from database. And when I don't edit the login textfield it's data remains as string since there was no value change, so I'm forced to use a string value as reference to my logout picker.
the result login being: 1999-12-31 23:00:00 +0000
and logout: 2018-01-04 22:24:20 +0000
this is the reason why using the login as my minimum time didn't work
so here's what I did, I added the current date so I can use it.
#objc func timeOutPickerValueChanged(sender: UIDatePicker) {
if let actualLoginText = loginTextField.text {
let date = Date()
let start = actualLoginText
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
let nowTime = formatter.string(from: date)
let concat = nowTime + " " + start
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm a"
formattedTime = dateFormatter.date(from: concat)!
}
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
timeOutPicker.minimumDate = Calendar.current.date(byAdding: .minute, value: 5, to: formattedTime)
logoutTextField.text = formatter.string(from: sender.date)
}

UITextField not triggering action event upon UIDatePicker action event

I have a datePicker that updates my textField. Once the textField data changes is supposed to perform actions(such as verifying data). The problem is that after the datePicker updates the textField, the textField does not perform any actions, regardless of what action event I am using on the textField, such as .ValueChanged.I cannot figure out what the problem is. I noticed though, that if I am to type something in the textField, the action event works.
Here's my code:
class CustomTextField: UITextField, UITextFieldDelegate {
var customDelegate : CustomTextFieldDelegate?
enum type {
case standard
case date
}
var textFieldType : type = .standard {
didSet{
switch self.textFieldType {
case .standard:
break
case .date:
let datePicker = UIDatePicker.MMMddyyyy
self.inputView = datePicker
datePicker.addTarget(self, action: #selector(refreshOnDatePicker(_:)), for: .valueChanged)
self.addTarget(self, action: #selector(checkFieldInput), for: .applicationReserved)
break
}
}
}
#objc private func checkFieldInput(sender: CustomTextField){
guard let fieldInput = self.text else { return }
switch sender.textFieldType {
case .standard:
break
case .date:
customDelegate?.isTextFieldVerified = !fieldInput.isEmpty
break
}
}
#objc private func refreshOnDatePicker(_ selector: UIDatePicker){
self.text = selector.date.formattedMMMMddyyyy
}
}
fileprivate extension UIDatePicker {
class var MMMddyyyy : UIDatePicker {
let currentDate: NSDate = NSDate()
let calendar: NSCalendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)!
calendar.timeZone = NSTimeZone(name: "UTC")! as TimeZone
let components: NSDateComponents = NSDateComponents()
components.calendar = calendar as Calendar
components.year = -100
let minDate: NSDate = calendar.date(byAdding: components as DateComponents, to: currentDate as Date, options: NSCalendar.Options(rawValue: 0))! as NSDate
let dp = UIDatePicker()
dp.datePickerMode = .date
dp.minimumDate = minDate as Date
dp.maximumDate = currentDate as Date
dp.backgroundColor = UIColor.white
return dp
}
}
fileprivate extension Date {
/// date formatted type "MMMM dd, yyyy"
var formattedMMMMddyyyy: String {
let formatter = DateFormatter()
formatter.dateFormat = "MMMM dd, yyyy"
return formatter.string(from: self)
}
}
Try this
#objc private func refreshOnDatePicker(_ selector: UIDatePicker){
self.text = selector.date.formattedMMMMddyyyy
self.checkFieldInput(sender:self)
}

How do I get the age after using a date picker?

I need to get the age after the datepicker has been used. How would I do this. I can currently get the datepicker to work.
#IBAction func datepickerobj(sender: UITextField) {
let datePickerView:UIDatePicker = UIDatePicker()
datePickerView.datePickerMode = UIDatePickerMode.Date
sender.inputView = datePickerView
datePickerView.addTarget(self, action: Selector("datePickerValueChanged:"), forControlEvents: UIControlEvents.ValueChanged)
}
func datePickerValueChanged(sender:UIDatePicker) {
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.MediumStyle
dateFormatter.timeStyle = NSDateFormatterStyle.NoStyle
dateofbirth.text = dateFormatter.stringFromDate(sender.date)
let myDOB = NSCalendar.currentCalendar().dateWithEra(1, year: 1970, month: 09, day: 10, hour: 0, minute: 0, second: 0, nanosecond: 0)!
let myAge = myDOB.age
}
Swift 4.1 / Xcode 9.4.1
func age(dateOfBirth: Date) -> Double {
var ageComponents: DateComponents = Calendar.current.dateComponents([.year], from: dateOfBirth, to: Date())
return Double(ageComponents.year!)
}
This will work fine for u
var birthday: NSDate = ..... //date that comes from date picker
var now: NSDate = NSDate()
var ageComponents: NSDateComponents = NSCalendar.currentCalendar().components(.Year, fromDate: birthday, toDate: now, options: 0)
var age: Int = ageComponents.year()
If u want to formate date you can use DateFormatter
Ex.
let usDateFormat = NSDateFormatter.dateFormatFromTemplate("MMddyyyy", options: 0, locale: NSLocale(localeIdentifier: "en-US"))
//usDateFormat now contains an optional string "MM/dd/yyyy".
let gbDateFormat = NSDateFormatter.dateFormatFromTemplate("MMddyyyy", options: 0, locale: NSLocale(localeIdentifier: "en-GB"))
//gbDateFormat now contains an optional string "dd/MM/yyyy"
formatter.dateFormat = usDateFormat
let usSwiftDayString = formatter.stringFromDate(swiftDay)
// usSwiftDayString now contains the string "06/02/2014".
formatter.dateFormat = gbDateFormat
let gbSwiftDayString = formatter.stringFromDate(swiftDay)
// gbSwiftDayString now contains the string "02/06/2014".
//may be it will help for u
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy"
let currentYear: Int = Int(formatter.stringFromDate(NSDate()))
let dobYear: Int = Int(formatter.stringFromDate(myDOB))
let age: Int = currentYear - dobYear
extension Date {
func age() -> Int {
return Int(Calendar.current.dateComponents([.year], from: self, to: Date()).year!)
}
}
An Date extension version, which let you call the method on any Date var. eg:
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([NSCalendarUnit.Year, NSCalendarUnit.Month, NSCalendarUnit.Day], fromDate: NSDate.init())
components.day = 31
components.month = 1
components.year = 1979
let date = calendar.dateFromComponents(components)
let years = date.age

Resources