UIView styling with functions
Today I want to talk about
UIView styling. The common approach when we want to customize the display of native UI controls (for instance
UILabel) is to create a subclass that overrides a bunch of properties. That works well most of the time, but some problems may arise.
The filled and rounded button
We you use subclasses in Swift to style your views, you loose composition. For example, if you create two
RoundedButton, how do you create a button that is both filled and rounded ?
One solution of this problem is to stop using subclasses, but to leverage Swift functions and type system.
We can think about a view style, as a function that consumes a view, and set some properties on it.
We can wrap this function into an object, for more control on it.
We can now create some styles for our filled and rounded button.
Now that we have our two styles for both filled and rounded buttons, we can create a new style for a rounded and filled button very easily.
What was previously impossible with
UIButton subclasses is now very straightforward using simple functions.
Now that we get the general idea, it’s time for syntactic sugar!
First of all, for now our styles live in the global namespace. That’s not very scalable.
The solution here is to extend
ViewStyle and to constrain the generic type.
That’s nice, we have a namespace to list all our styles. But it’s not very handy to style a button yet.
To improve this, we can define a function that is responsible to apply a style to an object, inferring the type of the style based on the type of the object.
Protocols to the rescue
The code looks good and is readable. But we can go one step further! I want to get rid of the global
style(_:with:) function, and to use an instance method of
UIButton instead. For this, let’s define an empty protocol
Stylable, and make
UIView conform to it. That way we will be able to add methods to
Stylable and all the
UIView subclasses will get them for free.
That may seem a little odd, but we can now extend
Stylable to add a method to apply a style to any
UIView subclasses gain this
apply(_:) method for free! The code becomes compact and readable.
What’s more, we can’t misuse our styles because of the Swift type system!
Init with style
With the previous
apply(_:) method you will often find yourself writing these two lines:
What if we could initialize our button (or any other
UIView) with a predefined style? It would save us one line of code each time.
That is possible, modifying slightly our
We can now use the following syntax:
With view styles as plain Swift functions, we achieved two things:
- first, a technical improvement on view subclasses: the composition of two
UIViewsubclasses was impossible, whereas it becomes very easy using Swift functions.
- second, an easier communication between developers and designers. Indeed, designers often work with styles, in order to reuse components and keep a consistent look and feel all around the app. If you can extract their styles and map them in a Swift file, it will become much simpler to develop your UI, and to update these styles in the future.
You can find the full gist here.