Swift subclass UIView
This is more simple.
override init (frame : CGRect) {
super.init(frame : frame)
// Do what you want.
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
It's important that your UIView can be created by interface builder/storyboards or from code. I find it's useful to have a setup
method to reduce duplicating any setup code. e.g.
class RedView: UIView {
override init (frame: CGRect) {
super.init(frame: frame)
setup()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
setup()
}
func setup () {
backgroundColor = .red
}
}
I usually do something like this, its a bit verbose.
class MyView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
addBehavior()
}
convenience init() {
self.init(frame: CGRect.zero)
}
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
func addBehavior() {
print("Add all the behavior here")
}
}
let u = MyView(frame: CGRect.zero)
let v = MyView()
(Edit: I've edited my answer so that the relation between the initializers is more clear)
Custom UIView Subclass Example
I usually create iOS apps without using storyboards or nibs. I'll share some techniques I've learned to answer your questions.
Hiding Unwanted init
Methods
My first suggestion is to declare a base UIView
to hide unwanted initializers. I've discussed this approach in detail in my answer to "How to Hide Storyboard and Nib Specific Initializers in UI Subclasses". Note: This approach assumes you will not use BaseView
or its descendants in storyboards or nibs since it will intentionally cause the app to crash.
class BaseView: UIView {
// This initializer hides init(frame:) from subclasses
init() {
super.init(frame: CGRect.zero)
}
// This attribute hides `init(coder:)` from subclasses
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
}
Your custom UIView subclass should inherit from BaseView
. It must call super.init() in its initializer. It does not need to implement init(coder:)
. This is demonstrated in the example below.
Adding a UITextField
I create stored properties for subviews referenced outside of the init
method. I would typically do so for a UITextField. I prefer to instantiate subviews within the declaration of the subview property like this: let textField = UITextField()
.
The UITextField will not be visible unless you add it to the custom view's subview list by calling addSubview(_:)
. This is demonstrated in the example below.
Programmatic Layout Without Auto Layout
The UITextField will not be visible unless you set its size and position. I often do layout in code (not using Auto Layout) within the layoutSubviews method. layoutSubviews()
is called initially and whenever a resize event happens. This allows adjusting layout depending on the size of CustomView. For example, if CustomView appears the full width on various sizes of iPhones and iPads and adjusts for rotation, it needs to accommodate many initial sizes and resize dynamically.
You can refer to frame.height
and frame.width
within layoutSubviews()
to get the CustomView's dimensions for reference. This is demonstrated in the example below.
Example UIView Subclass
A custom UIView subclass containing a UITextField which does not need to implement init?(coder:)
.
class CustomView: BaseView {
let textField = UITextField()
override init() {
super.init()
// configure and add textField as subview
textField.placeholder = "placeholder text"
textField.font = UIFont.systemFont(ofSize: 12)
addSubview(textField)
}
override func layoutSubviews() {
super.layoutSubviews()
// Set textField size and position
textField.frame.size = CGSize(width: frame.width - 20, height: 30)
textField.frame.origin = CGPoint(x: 10, y: 10)
}
}
Programmatic Layout with Auto Layout
You can also implement layout using Auto Layout in code. Since I don't often do this, I will not show an example. You can find examples of implementing Auto Layout in code on Stack Overflow and elsewhere on the Internet.
Programmatic Layout Frameworks
There are open source frameworks that implement layout in code. One I am interested in but have not tried is LayoutKit. It was written by the development team an LinkedIn. From the Github repository: "LinkedIn created LayoutKit because we have found that Auto Layout is not performant enough for complicated view hierarchies in scrollable views."
Why put fatalError
in init(coder:)
When creating UIView subclasses that will never be used in a storyboard or nib, you might introduce initializers with different parameters and initialization requirements that could not be called by the init(coder:)
method. If you did not fail init(coder:) with a fatalError
, it could lead to very confusing problems down the line if accidentally used in a storyboard/nib. The fatalError asserts these intentions.
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
If you want to run some code when the subclass is created regardless of whether it is created in code or a storyboard/nib then you could do something like the following (based on Jeff Gu Kang’s answer)
class CustomView: UIView {
override init (frame: CGRect) {
super.init(frame: frame)
initCommon()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initCommon()
}
func initCommon() {
// Your custom initialization code
}
}