How to customise UIDatePicker to achieve the following output? - ios

This is the picker that contains the day, date, and month as input, also, in one scroll, is there any way to achieve this kind of functionality from picker/datepicker in iOS?

For such a case, you should add it as a UIPickerView. Assuming that the issue would be: How to fill its values by the days of the current month? to make it more clear, I would mention it as steps:
You should get the current month start and end dates. You could find how to achieve it by checking this answer.
You will need to get all days -with the desired format- between the start and the end dates. You could find how to achieve it by checking this answer.
After getting all days in the current month, you could fill the date picker datasource method easily.
Implementation:
class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
var currentMonthDays: [String] {
var array = [String]()
var date = Date().startOfMonth() // first date
let endDate = Date().endOfMonth()
let fmt = DateFormatter()
//fmt.dateFormat = "dd/MM/yyyy"
fmt.dateFormat = "EEE dd MMM"
while date <= endDate {
date = Calendar.current.date(byAdding: .day, value: 1, to: date)!
array.append(fmt.string(from: date))
}
return array
}
override func viewDidLoad() {
super.viewDidLoad()
print(currentMonthDays)
}
// Picker View Data Soruce & delegate Methods
// REMARK: make sure to connect pickerview datasource/delegate to your view controller,
// I did it from the storyboard
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return currentMonthDays.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return currentMonthDays[row]
}
}
// Step #1:
extension Date {
func startOfMonth() -> Date {
return Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: Calendar.current.startOfDay(for: self)))!
}
func endOfMonth() -> Date {
return Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: self.startOfMonth())!
}
}

Do below steps :-
1. You have to change date with applying DateFormatter().
2. Save all dates (string format) in your NSArray or NSMutableArray for your UIPickerView Datasource method.
3. Then call UIPickerView Datasource and Delegate methods for fetch your result.
For date conversion you can apply below code.
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEE dd MMM"
let currentDate = NSDate()
let convertedDateString = dateFormatter.string(from:currentDate as Date)
print("convertedDate: ", convertedDateString)

Related

how counting down the days in swift?

I am new to Swift and I have not worked with NSDate. For my app I need how to make to calculate how many days there are until the event. The date of the event is written with DatePicker on Firebase, and I need to calculate from the current date how many days are left until the day it's written. All I need is to count down the days.
Convert your data from DatePicker to Date object, and you can use the following function which returns an Int, a representation of the number of days to a passed date.
func daysTo(date: Date) -> Int? {
let calendar = Calendar.current
let date1 = calendar.startOfDay(for: Date())
let date2 = calendar.startOfDay(for: date)
let components = calendar.dateComponents([.day], from: date1, to: date2)
return components.day
}
you can use this extension for find difference between date:
extension Date {
public func diffrenceTime() -> (Int, Int) {
var cal = Calendar.init(identifier: .persian)
let d1 = Date()
let components = cal.dateComponents([.hour, .minute], from: self, to: d1)
let diffHour = components.hour!
let diffMinute = components.minute!
return (diffHour, diffMinute)
}
public func fullDistance(from date: Date, resultIn component: Calendar.Component, calendar: Calendar = .current) -> Int? {
calendar.dateComponents([component], from: self, to: date).value(for: component)
}
public func distance(from date: Date, only component: Calendar.Component, calendar: Calendar = .current) -> Int {
let days1 = calendar.component(component, from: self)
let days2 = calendar.component(component, from: date)
return days1 - days2
}
public func hasSame(_ component: Calendar.Component, as date: Date) -> Bool {
self.distance(from: date, only: component) == 0
}
}
example for use:
let dateOne = Date() // or any date
let dateTwo = getDateFromServer // your second date for
// option One
let distanceDay = dateOne.fullDistance(from: dateTwo, resultIn: .day)
var cal = Calendar.current // for your calendar
cal.locale = Locale.init(identifier: "EN")
// option Two
let distanceDay = dateOne.fullDistance(from: dateTwo, resultIn: .day, calendar: cal)
you can set hour, minute or any Component for find difference instead of day in result parameter

Show only years in UIDatePicker in Swift [duplicate]

Is there a way to have a UIDatePicker show year only?
I have a little code that I was about to delete, but the snippet is better off if it is online somewhere. It's not amazing, but it is searchable!
Objective-C code to create an array of all years since 1960. Perfect for input into a UIPicker
//Get Current Year into i2
NSDateFormatter* formatter = [[[NSDateFormatter alloc] init]autorelease];
[formatter setDateFormat:#"yyyy"];
int i2 = [[formatter stringFromDate:[NSDate date]] intValue];
//Create Years Array from 1960 to This year
years = [[NSMutableArray alloc] init];
for (int i=1960; i<=i2; i++) {
[years addObject:[NSString stringWithFormat:#"%d",i]];
}
The UIPickerView data source and delegate methods:
- (NSInteger)numberOfComponentsInPickerView: (UIPickerView*)thePickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)thePickerView numberOfRowsInComponent:(NSInteger)component
{
return [years count];
}
- (NSString *)pickerView:(UIPickerView *)thePickerView
titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [years objectAtIndex:row];
}
Don't forget the declaration in the interface
//Data
NSMutableArray *years;
The out put of it is
Referenced from here
You can't do this with UIDatePicker, but you can with UIPicker.
You need to create an array for the years, and add them to the UIPicker using the UIPicker delegates
Here's a tutorial.
I cannot comment, but I was playing around with the answer by Wolvorin and I decided to do a way where the most recent year would be at the top of the Picker. Currently, his code goes oldest to most recent years.
All I changes was in my NSMutableArray setup in the viewDidLoad instead of his:
//Create Years Array from 1960 to This year
years = [[NSMutableArray alloc] init];
for (int i=1960; i<=i2; i++) {
[years addObject:[NSString stringWithFormat:#"%d",i]];
}
I used:
years = [[NSMutableArray alloc] init];
for (int i1=i2; i1<=i2 & i1>=1920; i1--) {
[years addObject:[NSString stringWithFormat:#"%d",i1]];
}
Which does the for in reversed order going from the most recent date, then subtracting one year (and adding it to the Array) until it gets to 1920.
My formula:
int i1=i2; i1<=i2 & i1>=1920; i1--
Just updating the existing answer in this post with Swift 3:
var yearsTillNow : [String] {
var years = [String]()
for i in (1970..<2018).reversed() {
years.append("\(i)")
}
return years
}
Just use this for your UIPickerView datasource.
It is not possible to set only Year and Month. Only these modes are available as of iOS 13.5.1
time: A mode that displays the date in hours, minutes, and (optionally) an AM/PM designation. The exact items shown and their
order depend upon the locale set. An example of this mode is [ 6 | 53
| PM ].
date: A mode that displays the date in months, days of the month, and years. The exact order of these items depends on the locale
setting. An example of this mode is [ November | 15 | 2007 ].
dateAndTime: A mode that displays the date as unified day of the week, month, and day of the month values, plus hours, minutes, and
(optionally) an AM/PM designation. The exact order and format of these
items depends on the locale set. An example of this mode is [ Wed Nov
15 | 6 | 53 | PM ].
countDownTimer: A mode that displays hour and minute values, for example [ 1 | 53 ]. The application must set a timer to fire at the
proper interval and set the date picker as the seconds tick down. UIDatePicker.Mode - developer.apple.com
You may build it using a custom UIPickerView.
class DatePickerViewController: UIViewController {
var dates = [Date]()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
var date = Date().addYear(n: -10)
let endDate = Date().addYear(n: 10)
repeat {
date = date.addMonth(n: 1)
dates.append(date)
} while date < endDate
let picker = UIPickerView()
picker.dataSource = self
picker.delegate = self
picker.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(picker)
picker.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
picker.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
picker.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
picker.heightAnchor.constraint(equalToConstant: 500).isActive = true
}
}
extension DatePickerViewController: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return self.dates.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
let date = self.dates[row]
return date.stringDate()
}
func view(forRow row: Int, forComponent component: Int) -> UIView? {
let label = UILabel()
return label
}
}
extension DatePickerViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let date = self.dates[row]
// Date that the user select.
print( date, date.stringDate())
}
}
extension Date {
public func addYear(n: Int) -> Date {
let calendar = Calendar.current
return calendar.date(byAdding: .year, value: n, to: self)!
}
public func addMonth(n: Int) -> Date {
let calendar = Calendar.current
return calendar.date(byAdding: .month, value: n, to: self)!
}
public func stringDate() -> String {
let calendar = Calendar.current
let dateFormatter = DateFormatter()
dateFormatter.timeZone = calendar.timeZone
dateFormatter.locale = calendar.locale
// Use YYYY to show year only.
// Use MMMM to show month only.
dateFormatter.setLocalizedDateFormatFromTemplate("MMMM YYYY")
let dateString = dateFormatter.string(from: self)
return dateString
}
}
Just change the following two lines- add/subtract as many years you wish to show.
var date = Date().addYear(n: -10)
let endDate = Date().addYear(n: 10)
I changed Sharukh Mastan's code slightly to always show the current year on top.
var formattedDate: String? = ""
let format = DateFormatter()
format.dateFormat = "yyyy"
formattedDate = format.string(from: date)
var yearsTillNow: [String] {
var years = [String]()
for i in (Int(formattedDate!)!-70..<Int(formattedDate!)!+1).reversed() {
years.append("\(i)")
}
return years
}
print(yearsTillNow)
This prints an array of years going back for 70 years which you can use as UIPickerView datasource

Swift FSCalendar add event based on array of date

In my case, I am trying to customise FSCalendar. Here, add multiple event in calendar options available but it is showing three dots. I need to do If two same date in the array need to show two dot also if three need to show three dot but above three also need to show only three dot. How to achieve this?
https://iosexample.com/a-fully-customizable-ios-calendar-library-compatible-with-objective-c-and-swift/
My Code
var datesWithEvent = ["02-09-2019", "03-09-2019", "07-09-2019", "09-09-2019"]
var datesWithMultipleEvents = ["03-09-2019", "03-09-2019", "02-09-2019", "09-09-2019"]
func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {
let dateString = self.dateFormatter.string(from: date)
if self.datesWithEvent.contains(dateString) {
return 1
}
if self.datesWithMultipleEvents.contains(dateString) {
return 3
}
return 0
}
func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, eventDefaultColorsFor date: Date) -> [UIColor]? {
let key = self.dateFormatter.string(from: date)
if self.datesWithMultipleEvents.contains(key) {
return [UIColor.magenta, appearance.eventDefaultColor, UIColor.black]
}
return nil
}
Here,Below answer for adjusting the FSCalendar dot UI count based on JSON array Data.
func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {
let dateString = self.dateFormatter.string(from: date)
if self.datesWithEvent.contains(dateString) {
return 1 // Here, We have to assign JSON count key value based on duplication it will increase dot count UI.
}
return 0
}

iOS swift: selecting a cell I want to select the entire row

I have a calendar, and I want to select an entire row (week) when I select a day.. this is my code so far:
//When a date is selected
func calendar(_ calendar: JTAppleCalendarView, didSelectDate date: Date, cell: JTAppleCell?, cellState: CellState) {
selectTheWeek(of: date)
setupLayoutCell(cell: cell, cellState: cellState)
}
func selectTheWeek(of date: Date) {
let starOfTheWeek = date.startOfWeek()
let endOfTheWeeK = date.endOfWeek()
calendarCollectionView.selectDates(from: starOfTheWeek, to: endOfTheWeeK)
}
extension Date {
func startOfWeek() -> Date {
let calendar = Calendar.autoupdatingCurrent
let currentDateComponents = calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)
return calendar.date(from: currentDateComponents)!
}
func endOfWeek() -> Date {
let cal = Calendar.autoupdatingCurrent
var component = DateComponents()
component.weekOfYear = 1
component.day = -1
return cal.date(byAdding: component, to: startOfWeek())!
}
}
the problem is that I'm having an infinite loop, and it's clear the reason.
But I don't know how to prevent it. Any help?
JTAppleCalender is an external library. It's an extension of a collectionView.
You can use 2 techniques to break the loop.
First:
calendarViewselectDates(from: starOfTheWeek, to: endOfTheWeeK, triggerSelectionDelegate: false)
By setting triggerSelectionDelegate to false, your delegate function didSelect will not be called.
Second:
If you are using MasterBranch code (which i'll be releasing in a week or so), you can know whether or not your selection is programmer initiated vs user initiated. You know this by --> cellState.
if cellState.selectionType == .programatic {
// ignore stuff
} else {
// Do stuff
}
You can put this if statement in your shouldSelect function.
I know this is a not the best possible solution but it could work to avoid your issue
//When a date is selected
var shouldSelectWeek = true
func calendar(_ calendar: JTAppleCalendarView, didSelectDate date: Date, cell: JTAppleCell?, cellState: CellState) {
if shouldSelectWeek{
selectTheWeek(of: date)
shouldSelectWeek = false
}
setupLayoutCell(cell: cell, cellState: cellState)
}
func selectTheWeek(of date: Date) {
let starOfTheWeek = date.startOfWeek()
let endOfTheWeeK = date.endOfWeek()
calendarCollectionView.selectDates(from: starOfTheWeek, to: endOfTheWeeK)
}
extension Date {
func startOfWeek() -> Date {
let calendar = Calendar.autoupdatingCurrent
let currentDateComponents = calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)
return calendar.date(from: currentDateComponents)!
}
func endOfWeek() -> Date {
let cal = Calendar.autoupdatingCurrent
var component = DateComponents()
component.weekOfYear = 1
component.day = -1
return cal.date(byAdding: component, to: startOfWeek())!
}
Just add a new boolean variable like var shouldIgnoreDateSelection = false and then you just do
func calendar(_ calendar: JTAppleCalendarView, didSelectDate date: Date, cell: JTAppleCell?, cellState: CellState) {
if shouldIgnoreDateSelection == false {
selectTheWeek(of: date)
}
setupLayoutCell(cell: cell, cellState: cellState)
}
func selectTheWeek(of date: Date) {
let starOfTheWeek = date.startOfWeek()
let endOfTheWeeK = date.endOfWeek()
shouldIgnoreDateSelection = true
calendarCollectionView.selectDates(from: starOfTheWeek, to: endOfTheWeeK)
shouldIgnoreDateSelection = false
}

How to Add events to FSCalendar Swift?

I am using FSCalendar to add calendar UI in my project . But I am unable to find any solution to add events to the calendar. So any ideas on how to add events to FSCalendar or any third party calendar framework?
To Set Events Base On Dates..
Instance of DateFormatter And Variables:
var datesWithEvent = ["2015-10-03", "2015-10-06", "2015-10-12", "2015-10-25"]
var datesWithMultipleEvents = ["2015-10-08", "2015-10-16", "2015-10-20", "2015-10-28"]
fileprivate lazy var dateFormatter2: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
next is... DataSource Function:
func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {
let dateString = self.dateFormatter2.string(from: date)
if self.datesWithEvent.contains(dateString) {
return 1
}
if self.datesWithMultipleEvents.contains(dateString) {
return 3
}
return 0
}
I Hope This Help You, This Code is Base On FsCalendar Sample Project.
I have an array that contains events's dates and the number of event on this date what I did is:
1- Comparing dates(comparing dates without time using compareDate(data.eventDate!, toDate: date, toUnitGranularity: .Day)) from my array with date(in the function)
2- If they're equal I'll add date to an array(datesWithEvent) without time so I can compare it later without problems
3- return true when it finds the right dates
var datesWithEvent:[NSDate] = []
func calendar(calendar: FSCalendar, hasEventForDate date: NSDate) -> Bool {
for data in eventsArray{
let order = NSCalendar.currentCalendar().compareDate(data.eventDate!, toDate: date, toUnitGranularity: .Day)
if order == NSComparisonResult.OrderedSame{
let unitFlags: NSCalendarUnit = [ .Day, .Month, .Year]
let calendar2: NSCalendar = NSCalendar.currentCalendar()
let components: NSDateComponents = calendar2.components(unitFlags, fromDate: data.eventDate!)
datesWithEvent.append(calendar2.dateFromComponents(components)!)
}
}
return datesWithEvent.contains(date)
}
And to precise the number of event on this date (different number of dots) I've added this code
func calendar(calendar: FSCalendar, numberOfEventsForDate date: NSDate) -> Int {
for data in eventsArray{
let order = NSCalendar.currentCalendar().compareDate(data.eventDate!, toDate: date, toUnitGranularity: .Day)
if order == NSComparisonResult.OrderedSame{
return data.numberOfEvent!
}
}
return 0
}
Try this code,in Objective-C
- (NSInteger)calendar:(FSCalendar *)calendar numberOfEventsForDate:(NSDate*)date
{
NSString *dateString = [calendar stringFromDate:date format:#"yyyy-MM-dd"];
if ([_datesWithEvent containsObject:dateString]){
return 1;
}
else{
NSLog(#"........Events List........");
}
return 0;
}
try these code it will help you
func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {
let dateString = self.dateFormatter.string(from: date)
print(dateString)
// datesForEvents : array of dates
if self.datesForEvents.contains(dateString) {
return 1
}
return 0

Resources