In the last tip, I showed you how to use UIPickerControllers
to make a time interval input. What I didn’t show you is how to output that. In this tip I’ll show you how to read and convert data from a picker that has a separate delegate.
If you download the exercise file, you’ll have a copy of the project as I left it last week. If you go to TimeComponents
, you can see the first problem: this is a delegate of a picker. It does not communicate directly with a view controller. I’m going to set a delegate for this that has two parameters: the time in a string and a time interval.
protocol TimeComponentsDelegate{
func didSelect(timeString:String,seconds:TimeInterval)
}
Then assign the delegate to TimeComponents
var delegate:TimeComponentsDelegate! = nil
In pickerView( didSelectRow:)
, I’ll add this to update the values whenever we change a component.
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
delegate.didSelect(timeString: resultString,seconds: timeInterval(resultString))
}
How do I get the String
and the TimeInterval
? It is easier to get the string first and convert it to seconds. I’m going to make a property resultString
.
Every selection, I’ll clear it.
resultString = ""
Then loop through the components
for index in 0..<componentMax.count{
}
Picker views have a method selectedRow(inComponent)
I’ll grab the selected digit in each component as I loop through
let digit = pickerView.selectedRow(inComponent: index)
If the component is a separator, I’ll add the separator to the string. Otherwise, I’ll add the digit.
if componentMax[index] == 0 {
resultString += separator
} else {
resultString += String(format:"%i",digit)
}
That gets me a string. I’ll use another method for converting the string to a timeInterval
. I will separate out the components by the separator, then add the value of the hours minutes and seconds together.
Under the didSelectRow
, add this
func timeInterval(_ timeString:String)-> TimeInterval!{
}
I’ll add an array of multipliers in seconds for hours (3600) minutes(60) and seconds. I’ll also initialize time
to 0.0
let multipliers = [3600.0,60.0,1.0]
var time = 0.0
I’ll split the string into an array using the separator to split it up.
var timeComponents = timeString.components(separatedBy: separator)
I’ll check to make sure the components array is the same length as the multipliers.
if timeComponents.count > multipliers.count {return nil}
.If so, I’ll loop through the components. I’ll use a guard while casting them to TimeInterval
That will also check for a valid value before multiplying by the multiplier. If invalid I’ll return nil
.
for index in 0..<timeComponents.count{
guard let timeComponent = TimeInterval(timeComponents[index]) else {return nil}
time += timeComponent * multipliers[index]
}
I’ll return the TimeInterval
after the loop.
return time
With all that in place. I’ll set up the delegates in the view controllers. I’ll get a message about the required method. Use the Fix there to load the required method. I’ll set it up to display timeString
.
class ViewController: UIViewController,TimeComponentsDelegate {
func didSelect(timeString: String,seconds:TimeInterval ) {
self.title = timeString
}
Don’t forget to point the delegate correctly.
timeComponents.delegate = self
Then I’ll do the same to the SecondViewController
, this time displaying the time interval:
class SecondViewController: UIViewController,TimeComponentsDelegate {
func didSelect(timeString: String, seconds: TimeInterval) {
title = String(format: "%6.0f seconds", seconds)
}
Note I’m living dangerously here, and not worrying about having nil
values. Don’t forget to point the delegate correctly.
timeComponents.delegate = self
Build and run. On the first view controller your time shows up on top.

Click the button, and try 1:01:01 on the second, and you get 3661 seconds.

Using the techniques from this and the last tip, you can make a powerful picker for selecting times.
The Whole Code
Here you’ll find the code for this project. You can also download it from GitHub here:
TimeComponents.swift
//
// TimeComponenets.swift
// ProtocolPickerDemo
//
//
// An exercise file for iOS Development Tips Weekly
// by Steven Lipton (C)2018, All rights reserved
// For videos go to http://bit.ly/TipsLinkedInLearning
// For code go to http://bit.ly/AppPieGithub
//
import UIKit
protocol TimeComponentsDelegate{
func didSelect(timeString:String,seconds:TimeInterval)
}
class TimeComponents: NSObject,UIPickerViewDataSource, UIPickerViewDelegate {
let componentMax = [9,9,0,5,9,0,5,9] // 0 = separator
var separator = ":"
var delegate:TimeComponentsDelegate! = nil
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return componentMax.count
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return componentMax[component] + 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if componentMax[component] == 0{
return separator
}
return String(format:"%i",row)
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
var resultsString = ""
for index in 0..<componentMax.count{
let digit = pickerView.selectedRow(inComponent: index)
if componentMax[index] == 0 {
resultsString += separator
} else {
resultsString += String(format:"%i",digit)
}
}
delegate.didSelect(timeString: resultsString, seconds: timeInterval(resultsString))
}
func timeInterval(_ timeString:String)-> TimeInterval!{
let multipliers = [3600.0,60.0,1.0]
var time = 0.0
var timeComponents = timeString.components(separatedBy: separator)
if timeComponents.count > multipliers.count {return nil}
for index in 0..<timeComponents.count{
guard let timeComponent = TimeInterval(timeComponents[index]) else {return nil}
time += timeComponent * multipliers[index]
}
return time
}
}
ViewController.swift
//
// ViewController.swift
// ProtocolPickerDemo
//
//
// An exercise file for iOS Development Tips Weekly
// by Steven Lipton (C)2018, All rights reserved
// For videos go to http://bit.ly/TipsLinkedInLearning
// For code go to http://bit.ly/AppPieGithub
//
import UIKit
class ViewController: UIViewController, TimeComponentsDelegate {
func didSelect(timeString: String, seconds: TimeInterval) {
self.title = timeString
}
@IBOutlet weak var picker: UIPickerView!
let timeComponents = TimeComponents()
override func viewDidLoad() {
super.viewDidLoad()
picker.delegate = timeComponents
picker.dataSource = timeComponents
timeComponents.delegate = self
}
}
SecondViewController.swift
//
// SecondViewController.swift
// ProtocolPickerDemo
//
// An exercise file for iOS Development Tips Weekly
// by Steven Lipton (C)2018, All rights reserved
// For videos go to http://bit.ly/TipsLinkedInLearning
// For code go to http://bit.ly/AppPieGithub
//
import UIKit
class SecondViewController: UIViewController, TimeComponentsDelegate{
func didSelect(timeString: String, seconds: TimeInterval) {
self.title = String(format:"%6.0f seconds",seconds)
}
@IBOutlet weak var picker: UIPickerView!
let timeComponents = TimeComponents()
override func viewDidLoad() {
super.viewDidLoad()
picker.delegate = timeComponents
picker.dataSource = timeComponents
timeComponents.delegate = self
}
}
Leave a Reply