Learn to design and construct reusable person interface parts through the use of customized view subclasses from the UIKit framework in Swift.
UIKit
The issue: UI, UX, design
Constructing person interfaces is the toughest a part of the job!
In a nutshell: design is a means of determining one of the best resolution that matches a selected drawback. Graphic design normally means the bodily drawing on a canvas or a paper. UX is actually how the person interacts with the appliance, in different phrases: the general digital expertise of the “buyer” journey. UI is the seen interface that he/she is going to see and work together with by touching the display screen. 👆
If I’ve to placed on the designer hat (and even the developer hat) I’ve to let you know that determining and implementing correct person interfaces is probably the most difficult drawback in a lot of the instances. Frontend techniques these days (cell, pill, even desktop apps) are simply fancy overlays on prime of some JSON information from a service / API. 🤷♂️
Why is it so arduous? Nicely, I imagine that if you wish to be an excellent designer, you want a correct engineering mindset as properly. You need to be able to observing the entire system (massive image), assemble constant UI parts (that really look the identical in all places), plan the desired expertise primarily based on the useful specification and lots of extra. It is also fairly a fundamental requirement to be an artist, suppose outdoors of the field, and be capable to clarify (describe) your concept to others. 🤯
Now inform me whose job is the toughest within the tech business? Yep, as a free of charge everyone seems to be a designer these days, additionally some firms do not rent this sort of specialists in any respect, however merely let the work performed by the builders. Anyway, let’s deal with how you can create good and reusable design implementations through the use of subclasses in Swift. 👍
Look, themes and types
Let me begin with a confession: I barely use the UIAppearance API. It is a private desire, however I prefer to set design properties like font
, textColor
, backgroundColor
straight on the view situations. Though in some instances I discovered the looks proxy very good, however nonetheless a little bit buggy. Possibly it will change with iOS 13 and the arrival of the lengthy awaited darkish mode.
Pricey Apple please make an auto swap primarily based on day / evening cycles (you recognize just like the sundown, dawn possibility within the house app). 🌙
- Model is a set of attributes that specifiy the looks for a single view.
- Theme is a set of comparable wanting view types, utilized to the entire utility.
These days I normally create some predefined set of styling parts, more than likely fonts, colours, however generally icons, and so on. I prefer to go together with the next construction:
Fonts
- title
- heading
- subheading
- physique
- small
Colours
Icons
You may have much more parts, however for the sake of simplicity let’s simply implement these ones with a very easy Swift resolution utilizing nested structs:
struct App {
struct Fonts {
static let title = UIFont.systemFont(ofSize: 32)
static let heading = UIFont.systemFont(ofSize: 24)
static let subheading = UIFont.systemFont(ofSize: 20)
static let physique = UIFont.systemFont(ofSize: 16)
static let small = UIFont.systemFont(ofSize: 14)
}
struct Colours {
static let title = UIColor.blue
static let heading = UIColor.black
static let background = UIColor.white
}
struct Icons {
static let again = UIImage(named: "BackIcon")!
static let share = UIImage(named: "ShareIcon")!
}
}
App.Fonts.title
App.Colours.background
App.Icons.again
This manner I get a reasonably easy syntax, which is sweet, altough this would possibly not let me do dynamic styling, so I can’t swap between gentle / darkish theme, however I actually do not thoughts that, as a result of in a lot of the instances it is not a requirement. 😅
Structs vs enums:
I may use enums as a substitute of structs with static properties, however on this case I just like the simplicity of this strategy. I do not wish to fiddle with uncooked values or extensions that accepts enums. It is only a private desire.
What if it’s important to assist a number of themes?
That is not an enormous difficulty, you possibly can outline a protocol to your wants, and implement the required theme protocol as you need. The true drawback is when it’s important to swap between your themes, as a result of it’s important to refresh / reload your complete UI. ♻️
There are some greatest practices, for instance you should utilize the NSNotificationCenter
class with a purpose to notify each view / controller in your app to refresh if a theme change happens. One other resolution is to easily reinitialize the entire UI of the appliance, so this implies you mainly begin from scratch with a model new rootViewController. 😱
Anyway, verify the hyperlinks beneath when you want one thing like this, however when you simply wish to assist darkish mode in your app, I would counsel to attend till the primary iOS 13 beta comes out. Possibly Apple will give some shiny new API to make issues simple.
Customized views as fashion parts
I promised styling by subclassing, so let’s dive into the subject. Now that we’ve got an excellent resolution to outline fonts, colours and different fundamental constructing blocks, it is time to apply these types to precise UI parts. In fact you should utilize the UIAppearance API, however for instance you possibly can’t merely set customized fonts via the looks proxy. 😢
One other factor is that I like consistency in design. So if a title is a blue, 32pt daring system font someplace in my utility I additionally count on that aspect to comply with the identical guideline in all places else. I clear up this drawback by creating subclasses for each single view aspect that has a customized fashion utilized to it. So for instance:
- TitleLabel (blue coloration, 32pt system font)
- HeadingLabel (blue coloration, 24pt system font)
- StandardButton (blue background)
- DestructiveButton (pink background)
One other good factor if in case you have subclasses and also you’re working with autolayout constraints from code, which you can put all of your constraint creation logic straight into the subclass itself. Let me present you an instance:
import UIKit
class TitleLabel: UILabel {
override init(body: CGRect) {
tremendous.init(body: body)
self.initialize()
}
required init?(coder aDecoder: NSCoder) {
tremendous.init(coder: aDecoder)
self.initialize()
}
init() {
tremendous.init(body: .zero)
self.initialize()
}
func initialize() {
self.translatesAutoresizingMaskIntoConstraints = false
self.textColor = App.Colours.title
self.font = App.Fonts.title
}
func constraints(in view: UIView) -> [NSLayoutConstraint] {
return [
self.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16),
self.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16),
self.centerYAnchor.constraint(equalTo: view.centerYAnchor),
]
}
}
As you possibly can see I solely need to set the font
& textColor
attributes as soon as, so after the view initialization is finished, I can ensure that each single occasion of TitleLabel
will look precisely the identical. The utilization is fairly easy too, you simply need to set the category identify in interface builder, or you possibly can merely create the view like this:
let titleLabel = TitleLabel()
self.view.addSubview(titleLabel)
NSLayoutConstraint.activate(titleLabel.constraints(in: self.view))
The factor I like probably the most about this strategy is that my constraints are going to be simply in the best place, so they will not bloat my view controller’s loadView methodology. You too can create a number of constraint variations primarily based in your present state of affairs with additional parameters, so it is fairly scalable for each state of affairs. 👍
View initialization is difficult
The draw back of this resolution is that view initialization is sort of tousled, due to the interface builder assist. You need to subclass each single view kind (button, label, and so on) and actually copy & paste your initialization strategies many times. I have already got some articles about this, verify the hyperlinks beneath. 👇
With the intention to clear up this drawback I normally find yourself by making a mother or father class for my very own styled views. Right here is an instance for an summary base class for my labels:
class Label: UILabel {
override init(body: CGRect) {
tremendous.init(body: body)
self.initialize()
}
required init?(coder aDecoder: NSCoder) {
tremendous.init(coder: aDecoder)
self.initialize()
}
init() {
tremendous.init(body: .zero)
self.initialize()
}
func initialize() {
self.translatesAutoresizingMaskIntoConstraints = false
}
}
So to any extent further I simply need to override the initialize
methodology.
class TitleLabel: Label {
override func initialize() {
tremendous.initialize()
self.font = App.Fonts.title
self.textColor = App.Colours.title
}
}
See, it is so significantly better, as a result of I haven’t got to cope with the required view initialization strategies anymore, additionally autoresizing will likely be off by default. ❤️
My last takeaway from this lesson is that you just shouldn’t be afraid of lessons and object oriented programming if it involves the UIKit framework. Protocol oriented programming (additionally useful programming) is nice when you use it in the best place, however since UIKit is sort of an OOP framework I imagine it is nonetheless higher to comply with these paradigms as a substitute of selecting some hacky manner. 🤪
If you happen to like my put up, please additionally comply with me on twitter, and subscribe to my month-to-month e-newsletter. It is 100% Swift solely content material, no spam ever!