Adding a Design Language to your Xcode project.
Intro.
We all have seen this type of code with several lines just to set component style, and we know how hard is to maintain due to various types of problems like:
- Lots of boilerplate code and difficult reading.
- Bigger and complex views.
- And most of all: not reusable/scalable.
We can easily overcome all this issues by defining an easy design language that we can use in our project..
A design language or design vocabulary is an overarching scheme or style that guides the design of a complement of products or architectural settings.
On this article we will provide a simple approach to add a design language to your project (text, colors and distances/margins). The same principles we will approach can be applied to all kinds of UIKit components and so dramatically reduce boilerplate code while improving readability and scalability.
What do we need?
- Understand that entity names (on your design language) target (correlate with) actions and not values.
- Choose what’s the identity you want for your app (colors, fonts, iconography, etc)
- Implement your design language.
I — Entity names target actions, not values.
Keep in mind that the design language naming should correlate with the possible actions in the app, instead of the design values.
We can explain this with a small example. Let’s start by defining our design language creating a basic color palette:
struct AppColorsV1 {
static let fireRed = UIColor(red: 1, green: 0, blue: 0)
static let oceanBlue = UIColor(red: 0, green: 165/255, blue:1)
}
Everything looks good but… today our destructive action (like delete account) has color that we call fireRed
, because is actually a red value color. But what if tomorrow our destructive action color is some kind of orange/magenta and not red anymore? So, maybe, fireRed
maybe is not the best name since it’s based on a value (color) and not on an action.
How about we call it danger
or destructive
instead of fireRed
?
II — Creating your app identity.
Colors
The hard part here is to choose your color palette. You can try online services like Coolors.
After you choose your color palette, you can implement it (using action names instead of value names) in several ways. Bellow is one of the many implementations options:
Now we can do things like:
typealias AppColors = UIColor.Pack3
.
.
let btnDeleteAccount = UIButton()
btnDeleteAccount.backgroundColor = AppColors.danger.color
.
.
Fonts
The same thinking is applied to Fonts/Styles for text components.
We can define our text language as the following:
Notice that we don’t use things like “titleFont”, or “appDefaultTextStyle”, we try to use action names instead of value names.
typealias AppFonts = UIFont.MyFonts
.
.
let lblTitle = UILabel()
lblTitle.font = AppFonts.caption.rawValue
.
.
III — Implementing your design language.
At this point we have all the bricks to build our house.
We started with:
let lblTitle = UILabel()
lblTitle.textColor = UIColor.black
lblTitle.font = UIFont.boldSystemFont(ofSize: 25)
and here we are with:
let lblTitle = UILabel()
lblTitle.textColor = AppColors.onPrimary.color
lblTitle.font = AppFonts.Styles.headingBold.rawValue
Still a bit verbose, but at least it’s scalable, more resilient when faced with code/design changes and definitely more elegant.
Improvements: Font + Color + … = Style
On the above example, we know that all our title labels are defined with:
- A font AppFonts.Styles.headingBold.
- The text color AppColors.onPrimary.
We can end up defining all of our title labels like:
let lblTitle = UILabel()
lblTitle.textColor = AppColors.onPrimary.color
lblTitle.font = AppFonts.Styles.headingBold.rawValue
lblTitle.backgroundColor = AppColors.surface.color
lblTtitle.textAlignment = .center
and end up falling again on the verbose (dark) side of the code (ohhhh boy, I love Star Wars…)
We can easily encapsulate this common behavior by defining an enum with the styles of our app, and the styles can then encapsulate all the properties at once.
With a simple implementation like the above, and we can now do things like:
let lblTitle = UILabel()
lblTitle.layoutStyle = .title
view.addSubview(lblTitle)
The LabelStyle
with value title
still defines only a text font and text color, but the total properties we can now set at once are endless.
Back to our example, we can now replace the color and font set, by a single line. But, can we do better?
Improvements: UIKitFactory.
Factory Pattern: A design pattern where an object is created without exposing the creation logic to the client. The factory pattern lets a class defer instantiation to subclasses.
We can still reduce the code verbosity by creating a factory class/struct/other.
The factory can be used to reduce verbosity and to act as a central gateway to create our UI elements, and also as a self documented way for developers to know what is available on the app to (re) use.
Back to our example, and using the factory we end up with much more clear and concise code.
Notes
- Notice that we also added
AppSizes
to our Design Language. - We are passing
textAlignment
property as parameter to our factory. We did it just as an example because that property could easily be stetted as as style defined property (see Code 3)
IV — Final considerations.
What we did.
- We added a simple design language that defines our app basic brand (Fonts and Colors). We defined also a design language for Sizes (see last image).
What we can do more.
- The same line of thoughts we followed for labels, can easily be adapted for any UIKit component (buttons).
V — Materials.
Playground
The playground used on this article can be found here.
RJSLibUF
RJSLibUF provides a free design language ready to use with: SizeNames, Fonts, LabelStyles, ButtonStyles, Color Paletts and more for a ready to use on a quick start project.