Custom Control Open Source in Swift

As a developer sometimes can be very frustrating to handle design requirements, more if they require creating UI that not only match the designs but also that can dynamically change based on server data, last week working on a small feature of an app I had to create a control that looks like a segmented control but also can look like a UISwitch, it will show items for user selection coming from a server, and the UI should adapt itself to show them properly, it should handle strings, images or colors and of course it must be easy to customize…well, at that point I started to worry and try to find how much of customization can I add to a UISwitch or a UISegmentedControl.

UIKit provides built in elements with limited customization, that’s why I decided to create my own control from scratch, lucky for us with UIControl it’s not that hard to accomplish pretty much every design.

UIControl is The base class for controls, which are visual elements that convey a specific action or intention in response to user interactions, they have methods that will react based on any type of event, on this custom control I used just this function to know which element was selected…

sendActions(for: .valueChanged)

I also used generics and protocols with associated types to create a struct that will help you return a value depending on the index selected, Pretty much all the information you need is commented on the project so I won’t explain it step by step how to create the control itself on the post but I will show you what is capable to do…

First, You can customize it in many ways, like on this examples…

Here are just 8 examples of customization, the control has a generic method called “setSegmentedWith(items: T)” that will populate your selector based on the type, here is a quick disclaimer… you will need to set some boolean properties to adapt the control to handle each type (i.e String, UIColor, UIImages) and all the examples for this customizations are in the source code for each of this controls.

Second, the project has a struct with a generic type called GenericViewModel it also conforms to a protocol with an associated type called IndexReurnable, it will help you get a value based on the index of the item selected, the only thing that you need to do is set a property of this type in your View, like this example for a control that shows colors …

private var colorsGenericViewModel: GenericViewModel<UIColor>?

Then you need to pass the same amount of items that your selector has, like in example 8 in the project…

@IBOutlet weak var colorsSegmentedControl: CustomSegmentedControl! {
        didSet {            
//Set this booleans to adapt control
colorsSegmentedControl.itemsWithDynamicColor = true
colorsSegmentedControl.fillEqually = false
colorsSegmentedControl.roundedControl = true

let colors = DummyDataSource.colorItems()
colorsSegmentedControl.setSegmentedWith(items: colors)
colorsSegmentedControl.imageForItemWithDynamicColors = UIImage(named: "vignette")
colorsSegmentedControl.padding = 2
colorsSegmentedControl.thumbViewColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
colorsSegmentedControl.animationDuration = 0.2
colorsGenericViewModel = GenericViewModel<UIColor>(items: colors)
}
}

Remember, they need to have the same amount of items but not necessarily have to be the same, for example, your selector can show strings but based on each string you need to return a custom object, well, in the GenericViewModel instantiation you set the type to that object and pass an array of those, make sense?

And finally, you can update the content of your selector, on the project, there is a button at the bottom of the screen that will update the values of the selector with colors, this is the code that makes the update….

@IBAction func showExampleOfUpdate(_ sender: UIButton) {
update = !update
let colors = !update ? DummyDataSource.colorItems() : DummyDataSource.moreColorItems()
self.colorsSegmentedControl.updateSegmentedWith(items: colors)
//NOTE important to update the viewModel also with new items or app will crash because the index could be out of bounds
self.colorsGenericViewModel?.update(items: colors)
}

Just remember if your app needs a selector that updates like on this example, and also you are using the genericViewModel you “NEED TO UPDATE THE VIEWMODEL WITH THE NEW ITEMS” if not your app could crash because the index selected may be out of range.

Please excuse me for not give you more insights about the code on this post, my intention is to share with you something that may save you hours of investigation and also encourage you to create your own controls, all the documentation needed is commented on the source code so check it out.

Here is the full project, feel free to use it in your own apps, or give me any kind of suggestions for improvement, for more follow my blog on Medium.