0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[iOS] SwiftUIでUIKitのMKMapViewを使う方法(UIViewRepresentable)

Last updated at Posted at 2025-04-13

⭐️ はじめに

SwiftUIのMapKitを使ってマップを実装している途中、
病院や飲食店、公園などのあらゆる情報がデフォルトに地図上に表示されていて、
ごちゃごちゃしていると感じました。

(なんか汚くないですか?笑)


もし最小対応バージョンが17.0以上なら、
以下のコードで対応できます。

Map(
// 省略
)
.mapStyle(.standard(pointsOfInterest: .excludingAll))
/*
マップにカフェは表示したいなら、以下のコード
.mapStyle(.standard(pointsOfInterest: .including([.cafe])))
*/

上記のコードの結果

(綺麗になりましたねー)


しかし、私のプロジェクトの最小対応バージョンは16で、
iOS16ではSwiftUIのMapKitを使ってpointsOfInterestをカスタマイズする方法はなさそうです。
(🙋もし、方法ご存知の方いらっしゃいましたら、コメントお願いします!)

そのため、マップViewはUIViewRepresentableを使ってUIKitで実装し、
SwiftUIのViewで使う方法を採用することにしました。

👨‍💻 詳細

⭐️UIViewRepresentableとは?

UIViewRepresentableは、SwiftUIでUIKitのUIViewを使うためのプロトコルです。
これを使うことで、UIKitの機能をSwiftUIのViewに統合でき、SwiftUIでは実現が難しい部分を解決できます。

UIViewRepresentableプロトコルに準拠すると、makeUIViewとupdateUIViewという2つのメソッドを必ず実装する必要があります。

⭐️makeUIView(context:)

このメソッドはSwiftUIのViewが初めて表示されるときに一度だけ呼ばれます。
ここではUIKitのView(今回の場合はMKMapView)を初期化や設定します。

 struct CustomMapView: UIViewRepresentable {
    // 省略
    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView()
        
        //これのためにUIViewRepresentableを使いました!
        mapView.pointOfInterestFilter = MKPointOfInterestFilter(including: [.publicTransport])
        mapView.showsUserLocation = true
        mapView.setRegion(region, animated: true)
        
        return mapView
    }
    // 省略
}

⭐️updateUIView(_:context:)

このメソッドはSwiftUI側の状態が更新されるたびに呼び出されます。
@State、@Binding、@ObservedObject、@StateObject などの状態変数が変更されると、SwiftUIがViewを再レンダリングし、updateUIView が呼ばれます。

例えば、ユーザーが地図上で位置を移動し、その新しい位置を基準にデータを取得する場合、SwiftUI側の状態が変わることでupdateUIViewが呼ばれます。

  struct CustomMapView: UIViewRepresentable {
    // 省略
    func updateUIView(_ mapView: MKMapView, context: Context) {
        mapView.removeAnnotations(mapView.annotations)
        
        let annotations = places.map { place in
            let annotation = CustomAnnotation(place: place)
            annotation.coordinate = place.coordinate
            annotation.title = place.displayName
            return annotation
        }
        
        mapView.addAnnotations(annotations)
    }
    // 省略
}

上記の二つのメソッドだけを実装すれば十分な場合もありますが、
私の場合はMKMapViewのdelegateを使う必要があったため、Coordinatorを定義して対応しました。

⭐️Coordinatorとは?

SwiftUIでは、UIKitのdelegateやdataSourceのような仕組みを直接使うことができません。
そのため、UIViewRepresentableではmakeCoordinator()メソッドを使って、SwiftUIとUIKitの橋渡しをするCoordinatorクラスを定義する必要があります

Coordinatorの中でMKMapViewDelegateを実装することで

  • ピン(アノテーション)をタップしたときの処理
  • 地図が移動されたときの処理
  • ピンの見た目をカスタマイズする処理

などのことができます。


⭐️フルコード

struct CustomMapView: UIViewRepresentable {
     func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> MKMapView {
        // 省略
        mapView.delegate = context.coordinator // delegateコード追加
        return mapView
    }

    func updateUIView(_ mapView: MKMapView, context: Context) {
        // 省略
    }

    class Coordinator: NSObject, MKMapViewDelegate {
        var parent: CustomMapView

        init(_ parent: CustomMapView) {
            self.parent = parent
        }

        func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
            // ピン(アノテーション)をタップしたときの処理
        }

        func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
            // 地図が移動されたときの処理
        }

        func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            // ピンの見た目をカスタマイズする処理
        }
    }
}

上記で実装したCustomMapViewを以下のような形でSwiftUIで使用できます!

struct HomeView: View {
    var body: some View {
        CustomMapView()
    }
}

💬 まとめ

SwiftUIはどんどんアップデートされていますが、
企業の事情によりプロジェクトの最小対応バージョンを引き上げるのが難しい場合もあります。

そのため、SwiftUIだけで対応できない部分は
UIViewRepresentableを用いてUIKitの機能を活用することで解決できます。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?