How do I write a custom init for a UIView subclass in Swift?
Solution 1
The init(frame:)
version is the default initializer. You must call it only after initializing your instance variables. If this view is being reconstituted from a Nib then your custom initializer will not be called, and instead the init?(coder:)
version will be called. Since Swift now requires an implementation of the required init?(coder:)
, I have updated the example below and changed the let
variable declarations to var
and optional. In this case, you would initialize them in awakeFromNib()
or at some later time.
class TestView : UIView {
var s: String?
var i: Int?
init(s: String, i: Int) {
self.s = s
self.i = i
super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Solution 2
I create a common init for the designated and required. For convenience inits I delegate to init(frame:)
with frame of zero.
Having zero frame is not a problem because typically the view is inside a ViewController's view; your custom view will get a good, safe chance to layout its subviews when its superview calls layoutSubviews()
or updateConstraints()
. These two functions are called by the system recursively throughout the view hierarchy. You can use either updateContstraints()
or layoutSubviews()
. updateContstraints()
is called first, then layoutSubviews()
. In updateConstraints()
make sure to call super last. In layoutSubviews()
, call super first.
Here's what I do:
@IBDesignable
class MyView: UIView {
convenience init(args: Whatever) {
self.init(frame: CGRect.zero)
//assign custom vars
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
commonInit()
}
private func commonInit() {
//custom initialization
}
override func updateConstraints() {
//set subview constraints here
super.updateConstraints()
}
override func layoutSubviews() {
super.layoutSubviews()
//manually set subview frames here
}
}
Solution 3
Swift 5 Solution
You can try out this implementation for running Swift 5 on XCode 11
class CustomView: UIView {
var customParam: customType
var container = UIView()
required init(customParamArg: customType) {
self.customParam = customParamArg
super.init(frame: .zero)
// Setting up the view can be done here
setupView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView() {
// Can do the setup of the view, including adding subviews
setupConstraints()
}
func setupConstraints() {
// setup custom constraints as you wish
}
}
Solution 4
Here is how I do it on iOS 9 in Swift -
import UIKit
class CustomView : UIView {
init() {
super.init(frame: UIScreen.mainScreen().bounds);
//for debug validation
self.backgroundColor = UIColor.blueColor();
print("My Custom Init");
return;
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented"); }
}
Here is a full project with example:
Solution 5
Here is how I do a Subview on iOS in Swift -
class CustomSubview : UIView {
init() {
super.init(frame: UIScreen.mainScreen().bounds);
let windowHeight : CGFloat = 150;
let windowWidth : CGFloat = 360;
self.backgroundColor = UIColor.whiteColor();
self.frame = CGRectMake(0, 0, windowWidth, windowHeight);
self.center = CGPoint(x: UIScreen.mainScreen().bounds.width/2, y: 375);
//for debug validation
self.backgroundColor = UIColor.grayColor();
print("My Custom Init");
return;
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented"); }
}
Related videos on Youtube
Doug Smith
I'm a web designer playing around with iOS from England
Updated on July 08, 2022Comments
-
Doug Smith almost 2 years
Say I want to
init
aUIView
subclass with aString
and anInt
.How would I do this in Swift if I'm just subclassing
UIView
? If I just make a custominit()
function but the parameters are a String and an Int, it tells me that "super.init() isn't called before returning from initializer".And if I call
super.init()
I'm told I must use a designated initializer. What should I be using there? The frame version? The coder version? Both? Why? -
Wolf McNally almost 10 yearsThen by all means make them
var
. But the default best practice in Swift is to declare variableslet
unless there is a reason to declare themvar
. There was no such reason to do so in my code example above, hencelet
. -
Decade Moon over 9 yearsThis code doesn't compile. You need to implement the required initialiser
init(coder:)
. -
RaptoX almost 8 yearsthis would use the whole display for the View
-
J-Dizzle almost 8 yearsyep! If interested in a partial subview, let me know and I'll post this as well
-
Carter Medlin almost 8 yearsI like this answer the best, because having the fatalError means I don't have to put any code in the required init.
-
Ari Lacenski over 7 years@J-Dizzle, I'd like to see the solution for partial views.
-
mafiOSo about 7 yearsinteresting how this compiled years ago. Nowadays it complains under init(coder:) that "Property self.s not initialized at super.init call"
-
Wolf McNally about 7 yearsFixed example for Swift 3.1. Compiles under a playground importing UIKit.
-
mfaani about 7 yearsisn't your answer saying the opposite of the accepted answer? I mean you're doing the customization after
super.init
, but he said it should be done beforesuper.init
... -
LightNight almost 7 years
super.init()
must be called before initializing self params? -
surfrider almost 7 yearsIt shouldn't work: Use of 'self' in method call 'commonInit' before super.init initializes self
-
MH175 almost 7 yearsInitialize custom arguments after self.init call. Updated my answer.
-
surfrider almost 7 yearsBut what if you want to initialise some properties in
commonInit
method, but you can't place it aftersuper
in this case because you should initialise all properties BEFOREsuper
call. Lol it seems like dead loop. -
Wolf McNally almost 7 years@LightNight I made
s
andi
Optional to keep things simple here. If they were not optional, they would also need to be initialized in the required initializer. Making them optional means they get to benil
whensuper.init()
is called. If they weren't optional, they would indeed need to be assigned before calling super.init(). -
Mike Critchley over 6 yearsNice use of the fatalError() call. I was having to use optionals just to silence warnings from an initializer that wasn't even being used. This shut it right up! Thanks.
-
J-Dizzle over 6 yearsNo, OP states "it tells me that "super.init() isn't called before returning from initializer", i.e. customization can happen at any time within init(). Also to note, you cannot modify 'self' before calling super.init() so your contest is not valid. Interesting consideration though, was fun to poke and explore!
-
J-Dizzle over 6 years@Ari, For partial views see popupView, of height 35
-
ThomasW over 6 yearsThere is no need for that
return
or the semicolons. -
MH175 almost 6 yearsThis is how Swift initialization often works: search for "two-phase initialization". You can use implicitly unwrapped optionals, but I recommend against it. Your architecture, especially when dealing with views, should initialize all local properties. I've used this commonInit() method to hundreds of views now. It works
-
GranolaGuy over 5 yearsCan someone explain why it's okay to not give initial values to
self.i
andself.s
inint?(coder)
? I know that it compiles because swift options. Are we expected to initialize them elsewhere? Where? Why can't we inint?(coder)
like with the other inits? -
Wolf McNally over 5 years@GranolaGuy A bit off-topic, but the answer is that optional values have a default value of
nil
if you don't provide a specific one yourself. -
Jordan H over 3 yearsYou can also add
private init() { fatalError() }
to make sure your custom init must be used -
famfamfam over 3 yearsthis will cause crash if uiview is place into storyboard, which called init with coder