はじめに
これは、ReleaseビルドしたiOSアプリで MKMapViewDelegate
の下記のようなメソッドが呼び出されないことがある現象に遭遇し、腑には落ちないけどなんとか解消したというメモです。
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
状況
環境
- Xcode 10.2
- Swift 5
- iOS 11 or 12
(Xcode 10.1 + Swift 4.2 のころに、この問題は発生していませんでした)
実装
アプリ内に MKMapView
を表示するコントローラクラスが複数存在し、それらを抽象化した親クラス BaseMapViewController
が MKMapViewDelegate
を宣言していました。(宣言のみで実装は無し)
class BaseMapViewController: UIViewController, MKMapViewDelegate {
@IBOutlet weak var mapView: MKMapView!
// MKMapViewDelegateの実装はなし
}
子クラス FirstMapViewController
, SecondMapViewController
, ThirdMapViewController
... が実際にそれぞれの delegate メソッドを実装していましたが、Releaseビルドにおいて FirstMapViewController
の rendererFor
は呼び出されるのに、SecondMapViewController
の rendererFor
は呼び出されないという不思議現象。(Debugビルドでは想定通り SecondMapViewController
のメソッドも呼び出されていました)
class FirstMapViewController: BaseMapViewController {
func showOverlay() {
mapView.addOverlay(overlay, .aboveRoads)
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
// 呼び出される😇
return FirstRenderer()
}
}
class SecondMapViewController: BaseMapViewController {
func showOverlay() {
mapView.addOverlay(overlay, .aboveRoads)
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
// 呼び出されない❌☠️
return SecondRenderer()
}
}
class ThirdMapViewController: BaseMapViewController {
func showAnnotation() {
mapView.addAnnotation(annotation)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
// 呼び出される😇
return ThirdView()
}
}
class FourthMapViewController: BaseMapViewController {
func showAnnotation() {
mapView.addAnnotation(annotation)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
// 呼び出されない❌☠️
return FourthView()
}
}
*もちろん実際の実装はもっと複雑で、このコードだとおそらくちゃんと呼び出されると思うのですが、雰囲気が伝わればということでご了承ください。(なにしろ、実装を変更していないのに、突如呼び出されなくなってしまったので)
回避策
BaseMapViewController
で rendererFor
と viewFor
の空実装を定義し、それぞれの子クラスでそれらの delegate メソッドを override すると、リリースビルドでも正しく delegate メソッドが呼ばれるようになりました。
class BaseMapViewController: UIViewController, MKMapViewDelegate {
@IBOutlet weak var mapView: MKMapView!
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
// 空実装
return MKOverlayRenderer()
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
// 空実装
return nil
}
}
class FirstMapViewController: BaseMapViewController {
override func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
// 呼び出される😇
return FirstRenderer()
}
}
class SecondMapViewController: BaseMapViewController {
override func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
// 呼び出された!!⭕️😇
return SecondRenderer()
}
}
class ThirdMapViewController: BaseMapViewController {
override func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
// 呼び出される😇
return ThirdView()
}
}
class FourthMapViewController: BaseMapViewController {
override func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
// 呼び出された!!⭕️😇
return FourthView()
}
}