Time display as in system clock - ios

I want to make the display of time as in the iOS system clock (time in capital letters, and part of the day small but capital).
I know that I need the help of an attributed string, but I don’t know how to make it compatible with different time display formats (12 and 24 hours), different languages and a local place.

To get time in string representation according user locale, you can use:
let formatter = DateFormatter()
formatter.timeStyle = DateFormatter.Style.short //Or in short formatter.timeStyle = .short
let time = formatter.string(from: Date())
For example it returns you 23:47 for Russia, and 11:47 PM for US.
Then we can check if time format returned with day part for user locale. If so we set another font for last two characters in string, which is our day part.
if let range = time.range(of: formatter.amSymbol) ?? time.range(of: formatter.pmSymbol) {
let fontSize: CGFloat = 8.0
let nsRange = NSRange(range, in: time)
let attrString = NSMutableAttributedString(string: time)
attrString.addAttributes([.font: UIFont.systemFont(ofSize: fontSize)], range: nsRange)
label.attributedText = attrString
} else {
label.text = time
}

Related

Controlling accessibility voice over readout of numbers in iOS

I'm trying to get an accessibilityValue with a decimal number on a custom UIView to readout as "twenty point one", for example, similar to how voice over reads out the duration and keyframe values on the video trimmer when editing a video in the Photos app.
The default setup reads out the value as "twenty dot one". If I set the accessibilityAttributedLabel instead using the accessibilitySpeechPunctuation key, it reads as "twenty period one".
view.accessibilityAttributedLabel = NSAttributedString(string: "20.1", attributes: [.accessibilitySpeechPunctuation: true])
Without resorting to manually building a numeric string to read out, anyone know how to get the number to read saying "point" instead of "dot" or "period"?
Got it! Formatting a number using a NumberFormatter with a style of .spellOut will generate a string with the fully spelled out value. Not what we want for a label's text, but exactly what we want for an accessibility label.
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
let label = UILabel()
label.text = formatter.string(from: 20.1)
label.accessibilityLabel = formatter.string(from: 20.1)
// prints out "twenty point one"
print(label.accessibilityLabel)

Swift NumberFormatter formatting all values to 3 decimal places?

I have a large array of doubles, which have a varying number of decimal places, such as:
[11307.3, 1025.64, 1.27826, 1676.46, 0.584175, 183.792, 1.02237, 13.649, 0.472665, 127.604]
I am attempting to format the number so there are commas every thousand and the decimal places are not formatted to a specific number such as 3dp. The array should look like
[11,307.3, 1,025.64, 1.27826, 1,676.46, 0.584175, 183.792, 1.02237, 13.649, 0.472665, 127.604]
I have tried doing this by defining NumberFormatter as such:
let numberFormatter = NumberFormatter()
and then choosing decimal for style:
numberFormatter.numberStyle = NumberFormatter.Style.decimal
The values in the array are display in a table view, and when a user taps on for example the 2nd cell, in a new screen the value 1,025.64 would be displayed.
I used this code to do that:
var formattedPrice = numberFormatter.string(from: NSNumber(value:coinPriceDouble!))
self.coinPriceLbl.text = "\(coinTitleText!): \(Cryptocoin.instance.fiatSymbol)\(formattedPrice!)"
This works perfect for any value that does not have more than 3 decimal places.
If the user chose the 3rd value in the array, it would display 1.278 not 1.27826.
Is there any way to format these values with commas but not force them to a specific number of decimal places?
As vadian said, NumberFormatter is highly customisable.
Just play around its properties, like (you need to customise based on your needs):
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.maximumFractionDigits = 3
Here the explanation for NumberFormatter's maximumFractionDigits property and related.
Here instead a blog that explains all the related aspects of NumberFormatter A Guide to NSNumberFormatter.
EDIT
Put the following code in a Playground and observe the result:
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.maximumFractionDigits = 3
let formattedNumbers = [11307.3, 1025.64, 1.27826, 1676.46, 0.584175, 183.792, 1.02237, 13.649, 0.472665, 127.604].flatMap { number in
return numberFormatter.string(from: number)
}
print(formattedNumbers)
Link: https://stackoverflow.com/a/27571946/6655075 .
This solved my problem. As I had 3 values displaying, each from a different array, I would end up formatting all 3 whereas I only wanted to format 1 array.
extension Double {
static let twoFractionDigits: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
return formatter
}()
var formatted: String {
return Double.twoFractionDigits.string(for: self) ?? ""
}
}
I removed
var formattedPrice = numberFormatter.string(from: NSNumber(value:coinPriceDouble!))
And simply used
self.coinPriceLbl.text = "\(coinTitleText!): \(Cryptocoin.instance.fiatSymbol)\(coinPriceDouble!.formatted)"
Edit: As Dávid Pásztor mentioned, I only want to add the comma separator to the values which need it while still maintaining the precision of each value down to the last decimal value.
You could try setting the maximum fraction digits to a largish number.
numberFormatter.maximumFractionDigits = 15

How could I set UIDatePicker to show a day later from current date in swift?

I am trying to make an somewhat alarm app which has mandatory settings at first but the user can configure it by using the UIDatePicker.
The alarm app first has a defalut setting such as a week later from the current time, but it can be changed as the user scrolls the UIDatePicker. I searched a lot but cannot find a suitable answer.
can somebody please help me!
You can set the UIDatePicker date to your desired date time.
let numberOfDays = 6
datePicker.date = NSDate(timeIntervalSinceNow: (((24 * 60) * 60) * numberOfDays))
Hope it will help you..
Swift 4.1:
datePicker.date = Date(timeIntervalSinceNow: (((24 * 60) * 60) * numberOfDays))
Add this line of code when you create date picker.
datePicker.maximumDate = Calendar.current.date(byAdding: .day, value: 0, to: Date())
You can set value increase or decrease and complete your requirement.
let dateString = "Thursday, 19 Jan 2017"
let df = DateFormatter()
df.dateFormat = "eeee, dd MM yyyy"
let date = df.date(from: dateString)
if let unwrappedDate = date {
myDatePicker.setDate(unwrappedDate, animated: false) //set your own date
}

Formatting a date to be fixed-width without zero-padding

I have a UITableView, in which each row displays a date and time plus a message (screenshot below). I'm using a single attributed string to display the text in the row (which is necessary for the color difference).
Because I don't want to zero-pad the month, day, or hour, the date on the left-hand side of the colon is variable length, which causes the list of messages to look disorganized.
The result I want is for all the colons to line up vertically, so that the messages line up, despite the number of characters in the date being variable. What's the best way to accomplish this?
I've tried essentially space-padding (by detecting the length of the month, day, and hour), but the result still doesn't line up perfectly and can result in long (ugly looking) blocks of whitespace when all three (month, date, and hour) need to be padded. Perhaps there is a way to distribute this extra space amongst all the characters evenly?
Date formatting:
//choose format for the date
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "M/d h:mma: "
dateFormatter.amSymbol = "am"
dateFormatter.pmSymbol = "pm"
Entering into the view:
//first put in the date
let classHistoryCellText = NSMutableAttributedString.init(string: formattedDate)
classHistoryCellText.addAttribute(NSForegroundColorAttributeName, value: UIColor.init(hex: "#00000", alpha: 0.3), range: NSMakeRange(0, lengthOfDate))
//append what the message is
classHistoryCellText.append(NSMutableAttributedString.init(string: classBeingViewed.classHistory[indexPath.row]["event"] as! String))
classHistoryCellText.addAttribute(NSForegroundColorAttributeName, value: UIColor.init(hex: "#00000"), range: NSMakeRange(lengthOfDate, classHistoryCellText.length - lengthOfDate))
//bold the entire thing and make it size fontSize
classHistoryCellText.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: fontSize), range: NSMakeRange(0, classHistoryCellText.length))
classHistoryCellToDisplay.textLabel?.attributedText = classHistoryCellText
Result:
Try changing your date formatter to:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/dd hh:mma: " // this line is changed
dateFormatter.amSymbol = "am"
dateFormatter.pmSymbol = "pm"
It will make months, days and hours to always have two digits which will make all your dates the same width.
This will work unless you have a specific requirement to avoid displaying leading zeros. If this is the case, the easiest solution is to use two labels - one for date and one for status. Make date label a fixed width.

UIDatePicker Time Issues

I have a problem with my UIDatePicker:
When it first appears on the view as you'll see in the screenshot,
the initial value of the UIDatePicker says Today 3:00 pm, but if I click on the done button the label shows 3:07 ( which is the actual time ).
I want the Label to display the same time as the UIDatePicker.
here is my code:
override func viewDidLoad() {
super.viewDidLoad()
myDatePicker.minuteInterval = 15
}
#IBAction func datePickerAction(sender: AnyObject) {
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy HH:mm"
var strDate = dateFormatter.stringFromDate(myDatePicker.date)
self.selectedDate.text = strDate
}
You need to adjust the initial date to the discrete minute amounts at first. I am sure your date picker works just fine once you change the value, right? When you set the date picker to a date (like 3:07) but the date picker is set to show only increments of 15 minutes, it will show 3:00, but the set date is still 3:07.
let allUnits = NSCalendarUnit(rawValue: UInt.max)
let components = NSCalendar.currentCalendar().components(allUnits, fromDate: NSDate())
var minute = components.minute
minute = (minute / 15) * 15
components.minute = minute
let datePickerDate = NSCalendar.currentCalendar().dateFromComponents(components)
This always rounds the minutes downwards (while you might want to round up if the minute % 15 > 7 and then account for rounding up to 60), but you get the idea.

Resources