I thought it would be an interesting exercise to add a picker control (which Flex developers will know as a SpinnerList) to my dial based colour picker. The new control has a hard coded list of colours - if one of those colours is selected, it updates the RBG and CMYK dial controls and conversely, when either dial control creates a colour that is a match to the picker, it sets its selected item accordingly.
Rather than extending the UIPickerView, I opted for composition and added an instance my new component, ColorSpinner which is an extended UI control. UIPickerViews require two important properties - a delegate which implements the UIPickerViewDelegate protocol and a data source which implements the UIPickerViewDataSource protocol.
I've made the host component, ColorSpinner, both the data source and the delegate.
Unlike lists in Flex, the UIPickerView doesn't accept a collection, rather it invokes methods on the delegate to get data for particular rows in a data source. So, ColorSpinner has a constant array that contains NamedColor structures:
let colors = [
NamedColor(name: "Custom", color: UIColor.clearColor()),
NamedColor(name: "Red", color: UIColor.redColor()),
NamedColor(name: "Green", color: UIColor.greenColor()),
NamedColor(name: "Blue", color: UIColor.blueColor()),
[...]
func pickerView(pickerView: UIPickerView!, titleForRow row: Int, forComponent component: Int) -> String
{
returncolors[row].name
}
When the user makes a selection, pickerView() with didSelectRow is invoked, and I set the ColorSpinner's currentColor is set. The didSet observer on that property invokes sendActionsForControlEvents() so that the main view controller can respond:
func pickerView(pickerView: UIPickerView!, didSelectRow row: Int, inComponent component: Int)
{
currentColor = colors[row].color
}
That observer is also invoked when the controller sets the spinner's colour when one of the dial based pickers is changed. Then, I loop over the array, find any matches and the the UIPickerView's selected index. This is a simple loop that uses the enumerate keyword:
for (index, namedColor) inenumerate(colors)
{
if namedColor.color.description == currentColor.description
{
spinner.selectRow(index, inComponent: 0, animated: true)
}
}
The three controls (spinner, RGB dial control and CMYK dial control) all work together really well (although the UI is a bit ropey in portrait format and works best in landscape). The animated transition on the UIPickerView is a nice touch which I'll be looking to add to my dial controls soon.
As always, all the source code is available in my GitHub repository.