I'm creating a subclass of MKAnnotationView
in my project. It needs to have two properties for storing subviews which I need to initialize somewhere at the beginning.
MKAnnotationView
has one initializer listed in its documentation, initWithAnnotation:reuseIdentifier:
, so I figured I'd simply override that:
class PulsatingDotMarker: MKAnnotationView {
let innerCircle: UIView
let outerCircle: UIView
override init!(annotation: MKAnnotation!, reuseIdentifier: String!) {
innerCircle = ...
outerCircle = ...
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
...
}
But this causes a runtime exception:
fatal error: use of unimplemented initializer 'init(frame:)' for class 'PulsatingDotMarker'
Ok, so I guess initWithAnnotation:reuseIdentifier:
internally calls initWithFrame:
, so it's probably that one that I should override instead. Let's try that:
class PulsatingDotMarker: MKAnnotationView {
let innerCircle: UIView
let outerCircle: UIView
override init(frame: CGRect) {
innerCircle = ...
outerCircle = ...
super.init(frame: frame)
}
...
}
This however causes a compile error when creating the annotation view object:
Extra argument 'reuseIdentifier' in call
Hmm, so if I implement the (required) initializer initWithFrame:
, it now loses the default initializer initWithAnnotation:reuseIdentifier:
?
Maybe if I added an override of initWithAnnotation:reuseIdentifier:
that just calls super
it will be available again, will that work?
class PulsatingDotMarker: MKAnnotationView {
let innerCircle: UIView
let outerCircle: UIView
init!(annotation: MKAnnotation!, reuseIdentifier: String!) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
override init(frame: CGRect) {
innerCircle = ...
outerCircle = ...
super.init(frame: frame)
}
...
}
Nope, still not good - compile error:
Property 'self.innerCircle' not initialized at super.init call
Ok, what if I had an initWithFrame:
, but initialized the subviews in initWithAnnotation:reuseIdentifier:
? (But then what if someone just calls initWithFrame:
directly?...)
class PulsatingDotMarker: MKAnnotationView {
let innerCircle: UIView
let outerCircle: UIView
init!(annotation: MKAnnotation!, reuseIdentifier: String!) {
innerCircle = ...
outerCircle = ...
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
...
}
Not surprisingly, Swift protects me from that by telling me:
Property 'self.innerCircle' not initialized at super.init call
(this time in initWithFrame:
).
So what am I supposed to do? I can't create the subviews both here and there, right?
class PulsatingDotMarker: MKAnnotationView {
let innerCircle: UIView
let outerCircle: UIView
init!(annotation: MKAnnotation!, reuseIdentifier: String!) {
innerCircle = ...
outerCircle = ...
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
override init(frame: CGRect) {
innerCircle = ...
outerCircle = ...
super.init(frame: frame)
}
...
}
Wrong again, this actually works - even though I'm assigning a constant property twice in the same object (!).
How should this be done properly?
(Note: the class also included a required initWithCoder:
initializer that just calls fatalError
from the first example, but the object is never created from a storyboard.)
Unfortunately for MKAnnotationView
forces you to implement init(frame: CGRect)
which means you have to initialise all your instance variables in that method as well.
This article explains it a bit more
For variables that can only be initialised with passed in values you have to make those variables optional and set them to nil in the init(frame: CGRect)
.
The reason for this is that I suspect that MKAnnotationView is calling self.initWithFrame: rect
in its objective-C init method. This is so that if a subclass overrides the initWithFrame:(CGRect) rect
it will be called. However, this causes a problem in swift because if you declare a custom designated initialiser you do not inherit initialisers of the superclass. Therefore you have to implement the init(frame: CGRect)
in your subclass.
I have had the same problem with UITableViewController
. Its header looks to follow the same pattern. i.e two faliable designated initialisers.
It makes me very sad. But what can you do.
For my app, the solution I chose is to declare the subview as an optional and instantiate it in initFrame...
var innerCircle: UIView?
Here is my code...
class EventAnnotationView: MKPinAnnotationView
{
static var REUSE_ID = "EventAnnotationView"
var imageView: UIImageView?
override init(frame: CGRect)
{
super.init(frame: frame)
// Create subview for custom images
imageView = UIImageView(frame: CGRectMake(0, 0, 22, 22))
...
}
override init(annotation: MKAnnotation!, reuseIdentifier: String!)
{
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
required init(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
}
}
Feels like less of a hack :), but requires more code/work since the subview is an optional.
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With