Keywords: Swift | UIView | Custom Initialization
Abstract: This article provides an in-depth exploration of implementing custom initializers for UIView subclasses in Swift, focusing on best practices and common pitfalls. It analyzes errors such as "super.init() isn't called before returning from initializer" and "must use a designated initializer," explaining how to correctly implement init(frame:) and required init?(coder:) methods. The guide demonstrates initializing custom instance variables and calling superclass initializers, with supplementary insights from other answers on using common initialization functions and layout methods. Topics include initialization flow, Nib loading mechanisms, and the sequence of updateConstraints and layoutSubviews calls, offering a thorough resource for iOS developers.
Introduction
In iOS development, customizing UIView subclasses is a common task, especially when initializing with specific parameters like String and Int. Developers often encounter compiler errors such as "super.init() isn't called before returning from initializer" or "must use a designated initializer." These issues stem from incomplete understanding of Swift's initialization rules and the UIView class inheritance structure. Based on the best-practice answer, this article details how to correctly implement custom initializers, supplemented with additional technical insights.
Initialization Mechanism of UIView
As a subclass of UIResponder, UIView follows Swift's strict safety rules during initialization. Key initializers include:
init(frame: CGRect): This is the designated initializer forUIView, used to set the frame when creating views programmatically.init?(coder: NSCoder): This is a required initializer invoked when a view is loaded from a storyboard or Nib file. Since Swift mandates that subclasses implement this method, therequiredkeyword must be added.
In custom subclasses, if a new initializer is added (e.g., accepting String and Int parameters), it must correctly call the superclass's designated initializer. Otherwise, the compiler will report errors because Swift requires all stored properties to be initialized before completion and the superclass initializer to be called after subclass property initialization.
Implementing Custom Initializers
Referencing the best answer, here is a typical example of a UIView subclass that accepts String and Int parameters via a custom initializer:
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)
}
}In this example:
- The custom initializer
init(s: String, i: Int)first initializes the subclass propertiesself.sandself.i. This is crucial because Swift requires all subclass stored properties to be fully initialized before calling the superclass initializer. - It then calls
super.init(frame:), the designated initializer ofUIView. A defaultCGRectvalue is passed here, but developers can adjust the frame size as needed. - The
required init?(coder:)method simply calls the superclass implementation. When a view is loaded from Interface Builder, this method is invoked, and the custom initializer is not executed. Thus, if configuration depends onsandi, it should be handled inawakeFromNib()or at another appropriate time.
Note that properties are declared as optionals (var s: String? and var i: Int?), providing flexibility for deferred initialization in Nib-loading scenarios. If non-optional types are used, they must be assigned in all initializers, including init?(coder:).
Supplementary Techniques and Advanced Practices
Other answers offer valuable additions, particularly for complex view initialization:
- Common Initialization Function: Many developers recommend using a private method like
commonInit()to centralize initialization logic. This can be called in bothinit(frame:)andinit?(coder:)to avoid code duplication. For example:private func commonInit() { // Configure subviews, set defaults, etc. } - Layout Method Call Sequence: In custom views,
updateConstraints()andlayoutSubviews()are key methods in the layout cycle. The system calls these recursively to ensure proper view hierarchy layout. Note: InupdateConstraints(), set constraints before callingsuper.updateConstraints(); inlayoutSubviews(), callsuper.layoutSubviews()before adjusting subview frames. - Zero-Frame Initialization: For views added dynamically in view controllers,
CGRect.zerocan be used as the initial frame, as the parent view's layout system will adjust its size later. This can be implemented via a convenience initializer:convenience init(args: Whatever) { self.init(frame: CGRect.zero) // Assign custom variables }
Common Errors and Solutions
Developers frequently encounter errors such as:
- Not Calling super.init(): In custom initializers, the superclass designated initializer must be called before returning. The solution is to ensure
super.init(frame:)orsuper.init(coder:)is called after initializing subclass properties. - Incorrect Initialization Order: Swift requires subclass properties to be initialized before the superclass. If
super.init()is called first, the compiler will error. The correct order is to initializeself.sandself.ifirst, then callsuper.init(frame:). - Ignoring required init?(coder:): Since Swift 4,
UIViewsubclasses must implement this method, even if the body is empty. Otherwise, the compiler will report an error.
Conclusion
When implementing custom initializers for UIView subclasses in Swift, the core is understanding the initialization chain and the role of superclass designated initializers. By initializing subclass properties first, calling super.init(frame:), and implementing required init?(coder:), common compilation errors can be avoided. Combining best practices with common initialization functions and layout methods enables the creation of flexible and maintainable custom views. For views loaded from Nibs, handle custom parameters in awakeFromNib() to ensure consistent behavior across all scenarios.