Question or problem in the Swift programming language:
The problem with subclassing UITextView (and UICollectionView) is that designated constructor is “initWithFrame”. But in real life, when it loads from storyboard, initWithCoder will be called.
class BorderedTextView: UITextView { //will be called init(coder: NSCoder?){ //constant values here is not an option super.init(frame: CGRectMake(0,0,100,100), textContainer: nil) } //will not be called init(frame: CGRect, textContainer: NSTextContainer!) { super.init(frame: frame, textContainer: textContainer) } }
As result I cannot call any UI customisation code on init and provide any initialization value for Swift variables except defaults.
I suppose that problem can be temporary solved by extracting frame size from “coder”, but I didn’t found the key for it.
Any ideas better than hardcode frame values?
How to solve the problem:
Solution 1:
(From my above comments:) This looks like a Swift bug. initWithCoder:
is called when
a view (or view controller) is instantiated from a Storyboard or Nib file, and overriding
that method works in Objective-C:
- (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { // Initialization code } return self; }
But the equivalent Swift code
class BorderedTextView: UITextView { init(coder: NSCoder!) { super.init(coder: coder) } }
fails with the error message “must call a designated initializer of the superclass ‘UITextView'”.
This problem occurs with all subclasses of UIView
that have a
their own designated initializer (e.g. UITextView
, UICollectionView
).
On the other hand, the problem does not occur with subclasses of UILabel
, which
does not have a designated initializer.
The Swift language is very strict about calling the super classes’ designated initializer,
but there should be a way to override initWithCoder:
for all custom UIView
subclasses, so I consider this a Swift bug.
As a workaround, you can do the custom initialisation in
override func awakeFromNib() { super.awakeFromNib() // ... }
Update for Swift 1.2: This apparently has been fixed. The parameter
changed, it is no longer an implicitly unwrapped optional. So this
compiles and works as expected (tested with Xcode 6.4):
class BorderedTextView: UITextView { required init(coder: NSCoder) { super.init(coder: coder) // ... } }
Update for Swift 2 (Xcode 7): init(coder:)
is a failable
initializer now:
class BorderedTextView: UITextView { required init?(coder: NSCoder) { super.init(coder: coder) // ... } }
Solution 2:
A more complete answer for Swift 3:
class ValidatedTextField:UITextField, UITextFieldDelegate { required override init(frame: CGRect) { super.init(frame: frame) //custom code self.delegate = self } required init?(coder: NSCoder) { super.init(coder: coder) //custom code self.delegate = self }